Skip to main content

pito_codecs/media/front/
audio.rs

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}