1use std::path::Path;
2
3use rayon::prelude::*;
4
5use super::ffmpeg::Ffmpeg;
6use super::probe::{container_ffmpeg, probe};
7use super::route::{Caps, Prefs, pick_decode};
8use crate::media::{AudioBuffer, Codec, Error, Result, Route};
9
10pub fn audio_pcm(
11 path: &Path,
12 track: usize,
13 rate: u32,
14 mono: bool,
15 prefs: &Prefs,
16) -> Result<AudioBuffer> {
17 let channels = if mono { 1 } else { 2 };
18 audio_pcm_range(path, track, 0.0, f64::INFINITY, rate, channels, prefs)
19}
20
21pub fn audio_pcm_range(
22 path: &Path,
23 track: usize,
24 from: f64,
25 to: f64,
26 rate: u32,
27 channels: u16,
28 prefs: &Prefs,
29) -> Result<AudioBuffer> {
30 if rate == 0 || channels == 0 {
31 return Err(Error::Invalid(format!(
32 "a sample rate of {rate} with {channels} channels"
33 )));
34 }
35 if from.is_nan() || to.is_nan() || to < from {
36 return Err(Error::Invalid(format!("the range {from} to {to}")));
37 }
38 let picked = pick_decode(Codec::Aac, prefs, &Caps::detect(prefs))?;
39 if picked.route != Route::Ffmpeg {
40 match super::native::audio_range(path, track, from, to, rate, channels) {
41 Ok(buf) => return Ok(buf),
42 Err(e) if prefs.user_ffmpeg().is_none() => return Err(e),
43 Err(_) => {}
44 }
45 }
46 let f = container_ffmpeg(prefs)?;
47 let from = from.max(0.0);
48 let start = (from * rate as f64).round() as usize;
49 let end = to.is_finite().then(|| (to * rate as f64).round() as usize);
50 if end.is_some_and(|e| e <= start) {
51 return Ok(AudioBuffer::new(rate, channels));
52 }
53 let mut buf = ffmpeg_pcm(
54 &f,
55 path,
56 track,
57 (from > 0.0).then_some(from),
58 end.map(|_| to - from),
59 rate,
60 channels,
61 )?;
62 if let Some(e) = end {
63 buf.samples
64 .truncate(e.saturating_sub(start) * channels as usize);
65 }
66 Ok(buf)
67}
68
69pub(super) fn ffmpeg_pcm(
70 f: &Ffmpeg,
71 path: &Path,
72 track: usize,
73 from: Option<f64>,
74 seconds: Option<f64>,
75 rate: u32,
76 channels: u16,
77) -> Result<AudioBuffer> {
78 let mut args: Vec<String> = Vec::new();
79 if let Some(from) = from {
80 args.extend(["-ss".into(), format!("{from:.6}")]);
81 }
82 if let Some(seconds) = seconds {
83 args.extend(["-t".into(), format!("{seconds:.6}")]);
84 }
85 args.extend([
86 "-i".into(),
87 path.display().to_string(),
88 "-map".into(),
89 format!("0:a:{track}"),
90 "-vn".into(),
91 "-ac".into(),
92 channels.to_string(),
93 "-ar".into(),
94 rate.to_string(),
95 "-f".into(),
96 "f32le".into(),
97 "-".into(),
98 ]);
99 let raw = f.run_args(&args)?;
100 let mut buf = AudioBuffer::new(rate, channels);
101 buf.samples = raw
102 .chunks_exact(4)
103 .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
104 .collect();
105 Ok(buf)
106}
107
108pub fn audio_mix(
109 path: &Path,
110 gains: &[f32],
111 from: f64,
112 to: f64,
113 rate: u32,
114 prefs: &Prefs,
115) -> Result<AudioBuffer> {
116 if from.is_nan() || to.is_nan() || to < from {
117 return Err(Error::Invalid(format!("the range {from} to {to}")));
118 }
119 let tracks: Vec<(usize, f32)> = probe(path, prefs)?
120 .audio
121 .iter()
122 .map(|t| (t.index, gains.get(t.index).copied().unwrap_or(1.0)))
123 .filter(|&(_, g)| g > 0.0)
124 .collect();
125 let decoded: Vec<(AudioBuffer, f32)> = tracks
126 .par_iter()
127 .map(|&(t, g)| audio_pcm_range(path, t, from, to, rate, 2, prefs).map(|b| (b, g)))
128 .collect::<Result<_>>()?;
129 let longest = decoded.iter().map(|(b, _)| b.frames()).max().unwrap_or(0);
130 let frames = if to.is_finite() {
131 (((to * rate as f64).round() - (from.max(0.0) * rate as f64).round()).max(0.0) as usize)
132 .min(longest)
133 } else {
134 longest
135 };
136 let mut out = AudioBuffer::new(rate, 2);
137 let samples = frames
138 .checked_mul(2)
139 .ok_or_else(|| Error::Invalid(format!("the range {from} to {to}")))?;
140 out.samples = vec![0.0; samples];
141 for (b, g) in &decoded {
142 for (o, s) in out.samples.iter_mut().zip(&b.samples) {
143 *o += s * g;
144 }
145 }
146 Ok(out)
147}