1use matroska::{Audio, Matroska, Settings};
4use rand::Rng;
5use symphonia::core::audio::Channels;
6use symphonia::core::sample::SampleFormat;
7
8use log::{error, info, 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};
15use crate::dsp::effects::AudioEffect;
16
17#[derive(Debug, Clone)]
19pub struct Prot {
20 pub info: Info,
21 file_path: Option<String>,
22 file_paths: Option<Vec<Vec<String>>>,
23 file_paths_dictionary: Option<Vec<String>>,
24 track_ids: Option<Vec<u32>>,
25 track_paths: Option<Vec<String>>,
26 duration: f64,
27 play_settings: Option<PlaySettingsFile>,
28 impulse_response_spec: Option<ImpulseResponseSpec>,
29 impulse_response_tail_db: Option<f32>,
30 effects: Option<Vec<AudioEffect>>,
31}
32
33impl Prot {
34 pub fn new(file_path: &String) -> Self {
36 let info = Info::new(file_path.clone());
37
38 println!("Info: {:?}", info);
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 play_settings: None,
49 impulse_response_spec: None,
50 impulse_response_tail_db: None,
51 effects: None,
52 };
53
54 this.load_play_settings();
55 this.refresh_tracks();
56
57 this
58 }
59
60 pub fn new_from_file_paths(file_paths: &Vec<Vec<String>>) -> Self {
62 let mut file_paths_dictionary = Vec::new();
63 for file_path in file_paths {
66 for path in file_path {
67 if !file_paths_dictionary.contains(path) {
68 file_paths_dictionary.push(path.clone());
69 }
70 }
71 }
72
73 let info = Info::new_from_file_paths(file_paths_dictionary.clone());
74
75 let mut this = Self {
76 info,
77 file_path: None,
78 file_paths: Some(file_paths.clone()),
79 file_paths_dictionary: Some(file_paths_dictionary),
80 track_ids: None,
81 track_paths: None,
82 duration: 0.0,
83 play_settings: None,
84 impulse_response_spec: None,
85 impulse_response_tail_db: None,
86 effects: None,
87 };
88
89 this.refresh_tracks();
90
91 this
92 }
93
94 pub fn refresh_tracks(&mut self) {
101 let mut longest_duration = 0.0;
102
103 if let Some(file_paths) = &self.file_paths {
104 let mut track_paths: Vec<String> = Vec::new();
106 for file_path in file_paths {
107 let random_number = rand::thread_rng().gen_range(0..file_path.len());
108 let track_path = file_path[random_number].clone();
109
110 let index_in_dictionary = self
111 .file_paths_dictionary
112 .as_ref()
113 .unwrap()
114 .iter()
115 .position(|x| *x == track_path)
116 .unwrap();
117 let duration = self.info.get_duration(index_in_dictionary as u32).unwrap();
118
119 if duration > longest_duration {
120 longest_duration = duration;
121 self.duration = longest_duration;
122 }
123
124 track_paths.push(track_path);
125 }
126
127 self.track_paths = Some(track_paths);
128
129 return;
130 }
131
132 if !self.file_path.is_some() {
133 return;
134 }
135
136 let mut track_index_array: Vec<u32> = Vec::new();
137 match self.play_settings.as_ref() {
138 Some(play_settings) => match play_settings {
139 PlaySettingsFile::Legacy(file) => {
140 collect_legacy_tracks(
141 file.settings.inner(),
142 &mut track_index_array,
143 &mut longest_duration,
144 &self.info,
145 &mut self.duration,
146 );
147 }
148 PlaySettingsFile::V1(file) => {
149 collect_tracks_from_ids(
150 &file.settings.inner().tracks,
151 &mut track_index_array,
152 &mut longest_duration,
153 &self.info,
154 &mut self.duration,
155 );
156 }
157 PlaySettingsFile::V2(file) => {
158 collect_tracks_from_ids(
159 &file.settings.inner().tracks,
160 &mut track_index_array,
161 &mut longest_duration,
162 &self.info,
163 &mut self.duration,
164 );
165 }
166 PlaySettingsFile::Unknown { .. } => {
167 error!("Unknown file format");
168 }
169 },
170 None => {
171 warn!("No play_settings.json found; no tracks resolved.");
172 }
173 }
174
175 self.track_ids = Some(track_index_array);
176 }
177
178 pub fn get_effects(&self) -> Option<Vec<AudioEffect>> {
180 self.effects.clone()
181 }
182
183 fn load_play_settings(&mut self) {
184 println!("Loading play settings...");
185 let Some(file_path) = self.file_path.as_ref() else {
186 return;
187 };
188
189 let file = std::fs::File::open(file_path).unwrap();
190 let mka: Matroska = Matroska::open(file).expect("Could not open file");
191
192 let mut parsed = None;
193
194 for attachment in &mka.attachments {
195 if attachment.name == "play_settings.json" {
196 match serde_json::from_slice::<PlaySettingsFile>(&attachment.data) {
197 Ok(play_settings) => {
198 parsed = Some(play_settings);
199 break;
200 }
201 Err(err) => {
202 error!("Failed to parse play_settings.json: {}", err);
203 }
204 }
205 }
206 }
207
208 let Some(play_settings) = parsed else {
209 return;
210 };
211
212 info!("Parsed play_settings.json");
213
214 self.impulse_response_spec = parse_impulse_response_spec(&play_settings);
215 self.impulse_response_tail_db = parse_impulse_response_tail_db(&play_settings);
216
217 match &play_settings {
218 PlaySettingsFile::V1(file) => {
219 self.effects = Some(file.settings.inner().effects.clone());
220 }
221 PlaySettingsFile::V2(file) => {
222 self.effects = Some(file.settings.inner().effects.clone());
223 }
224 _ => {}
225 }
226
227 if let Some(effects) = self.effects.as_ref() {
228 info!(
229 "Loaded play_settings effects ({}): {:?}",
230 effects.len(),
231 effects
232 );
233 }
234
235 self.play_settings = Some(play_settings);
236 }
237
238 fn get_audio_settings(file_path: &str) -> Audio {
239 let file = std::fs::File::open(file_path).unwrap();
240
241 let symph = match get_probe_result_from_string(file_path) {
242 Ok(probed) => probed,
243 Err(err) => {
244 warn!("Failed to probe audio settings: {}", err);
245 return Audio {
246 sample_rate: 0.0,
247 channels: 0,
248 bit_depth: None,
249 };
250 }
251 };
252
253 let first_track = match symph.format.tracks().first() {
254 Some(track) => &track.codec_params,
255 None => {
256 warn!("No audio tracks found in {}", file_path);
257 return Audio {
258 sample_rate: 0.0,
259 channels: 0,
260 bit_depth: None,
261 };
262 }
263 };
264
265 let channels = {
266 let channels_option = first_track.channels.unwrap_or(Channels::FRONT_CENTRE);
267 channels_option.iter().count()
268 };
269
270 let mut bit_depth = None;
271
272 let bits_per_sample = first_track
273 .bits_per_sample
274 .or_else(|| sample_format_bits(first_track.sample_format));
275 if let Some(bits) = bits_per_sample {
276 bit_depth = Some(bits as u64);
277 }
278
279 let audio = Audio {
280 sample_rate: first_track.sample_rate.unwrap_or(0) as f64,
281 channels: channels as u64,
282 bit_depth,
283 };
284
285 audio
286
287 }
303
304 pub fn get_impulse_response_spec(&self) -> Option<ImpulseResponseSpec> {
306 self.impulse_response_spec.clone()
307 }
308
309 pub fn get_impulse_response_tail_db(&self) -> Option<f32> {
311 self.impulse_response_tail_db
312 }
313
314 pub fn get_container_path(&self) -> Option<String> {
316 self.file_path.clone()
317 }
318
319 pub fn set_impulse_response_spec(&mut self, spec: ImpulseResponseSpec) {
321 self.impulse_response_spec = Some(spec);
322 }
323
324 pub fn set_impulse_response_tail_db(&mut self, tail_db: f32) {
326 self.impulse_response_tail_db = Some(tail_db);
327 }
328
329 pub fn get_keys(&self) -> Vec<u32> {
331 if let Some(track_paths) = &self.track_paths {
333 return (0..track_paths.len() as u32).collect();
334 }
335
336 if let Some(track_ids) = &self.track_ids {
337 return (0..track_ids.len() as u32).collect();
338 }
339
340 Vec::new()
341 }
342
343 pub fn get_ids(&self) -> Vec<String> {
345 if let Some(track_paths) = &self.track_paths {
346 return track_paths.clone();
347 }
348
349 if let Some(track_ids) = &self.track_ids {
350 return track_ids.into_iter().map(|id| format!("{}", id)).collect();
351 }
352
353 Vec::new()
354 }
355
356 pub fn enumerated_list(&self) -> Vec<(u16, String, Option<u32>)> {
358 let mut list: Vec<(u16, String, Option<u32>)> = Vec::new();
359 if let Some(track_paths) = &self.track_paths {
360 for (index, file_path) in track_paths.iter().enumerate() {
361 list.push((index as u16, String::from(file_path), None));
362 }
363
364 return list;
365 }
366
367 if let Some(track_ids) = &self.track_ids {
368 for (index, track_id) in track_ids.iter().enumerate() {
369 list.push((
370 index as u16,
371 String::from(self.file_path.as_ref().unwrap()),
372 Some(*track_id),
373 ));
374 }
375
376 return list;
377 }
378
379 list
380 }
381
382 pub fn container_track_entries(&self) -> Option<(String, Vec<(u16, u32)>)> {
384 let file_path = self.file_path.as_ref()?;
385 let track_ids = self.track_ids.as_ref()?;
386 let mut entries = Vec::new();
387 for (index, track_id) in track_ids.iter().enumerate() {
388 entries.push((index as u16, *track_id));
389 }
390 Some((file_path.clone(), entries))
391 }
392
393 pub fn get_duration(&self) -> &f64 {
395 &self.duration
396 }
397
398 pub fn get_length(&self) -> usize {
400 if let Some(file_paths) = &self.file_paths {
401 return file_paths.len();
402 }
403
404 if let Some(track_ids) = &self.track_ids {
405 return track_ids.len();
406 }
407
408 0
409 }
410
411 pub fn get_file_paths_dictionary(&self) -> Vec<String> {
413 match &self.file_paths_dictionary {
414 Some(dictionary) => dictionary.to_vec(),
415 None => Vec::new(),
416 }
417 }
418}
419
420fn collect_tracks_from_ids(
421 tracks: &[SettingsTrack],
422 track_index_array: &mut Vec<u32>,
423 longest_duration: &mut f64,
424 info: &Info,
425 total_duration: &mut f64,
426) {
427 for track in tracks {
428 if track.ids.is_empty() {
429 continue;
430 }
431 let random_number = rand::thread_rng().gen_range(0..track.ids.len());
432 let index = track.ids[random_number];
433 if let Some(track_duration) = info.get_duration(index) {
434 if track_duration > *longest_duration {
435 *longest_duration = track_duration;
436 *total_duration = *longest_duration;
437 }
438 }
439 track_index_array.push(index);
440 }
441}
442
443fn collect_legacy_tracks(
444 settings: &PlaySettingsLegacy,
445 track_index_array: &mut Vec<u32>,
446 longest_duration: &mut f64,
447 info: &Info,
448 total_duration: &mut f64,
449) {
450 for track in &settings.tracks {
451 let (Some(starting_index), Some(length)) = (track.starting_index, track.length) else {
452 continue;
453 };
454 let starting_index = starting_index + 1;
455 let index = rand::thread_rng().gen_range(starting_index..(starting_index + length));
456 if let Some(track_duration) = info.get_duration(index) {
457 if track_duration > *longest_duration {
458 *longest_duration = track_duration;
459 *total_duration = *longest_duration;
460 }
461 }
462 track_index_array.push(index);
463 }
464}
465
466fn sample_format_bits(sample_format: Option<SampleFormat>) -> Option<u32> {
467 match sample_format {
468 Some(SampleFormat::U8 | SampleFormat::S8) => Some(8),
469 Some(SampleFormat::U16 | SampleFormat::S16) => Some(16),
470 Some(SampleFormat::U24 | SampleFormat::S24) => Some(24),
471 Some(SampleFormat::U32 | SampleFormat::S32 | SampleFormat::F32) => Some(32),
472 Some(SampleFormat::F64) => Some(64),
473 None => None,
474 }
475}