Skip to main content

ffcine_audio/
buffer.rs

1//! Planar `f32` sample buffers and WAV I/O.
2
3use std::path::Path;
4
5use ffcine_core::{Error, Result};
6
7/// Planar audio: one `Vec<f32>` per channel, all the same length.
8#[derive(Debug, Clone, PartialEq, Default)]
9pub struct Audio {
10    pub rate: u32,
11    pub channels: Vec<Vec<f32>>,
12}
13
14impl Audio {
15    /// `channels` channels of `frames` samples of silence.
16    pub fn silence(rate: u32, channels: usize, frames: usize) -> Self {
17        Self {
18            rate,
19            channels: vec![vec![0.0; frames]; channels],
20        }
21    }
22
23    pub fn mono(rate: u32, samples: Vec<f32>) -> Self {
24        Self {
25            rate,
26            channels: vec![samples],
27        }
28    }
29
30    /// Samples per channel.
31    pub fn frames(&self) -> usize {
32        self.channels.first().map_or(0, Vec::len)
33    }
34
35    pub fn seconds(&self) -> f64 {
36        self.frames() as f64 / f64::from(self.rate.max(1))
37    }
38
39    /// The average of all channels.
40    pub fn to_mono(&self) -> Vec<f32> {
41        match self.channels.as_slice() {
42            [] => Vec::new(),
43            [one] => one.clone(),
44            chans => {
45                let k = 1.0 / chans.len() as f32;
46                (0..self.frames())
47                    .map(|i| chans.iter().map(|c| c[i]).sum::<f32>() * k)
48                    .collect()
49            }
50        }
51    }
52
53    /// Mono is copied to both sides; more than two channels fold down to the first two.
54    #[must_use]
55    pub fn to_stereo(&self) -> Self {
56        let channels = match self.channels.as_slice() {
57            [] => vec![Vec::new(), Vec::new()],
58            [one] => vec![one.clone(), one.clone()],
59            [l, r, ..] => vec![l.clone(), r.clone()],
60        };
61        Self {
62            rate: self.rate,
63            channels,
64        }
65    }
66
67    /// Multiplies every sample by `10^(db/20)`.
68    pub fn gain_db(&mut self, db: f64) {
69        let g = db_to_lin(db) as f32;
70        for c in &mut self.channels {
71            for s in c {
72                *s *= g;
73            }
74        }
75    }
76
77    /// Adds `other` (same channel count) into `self` starting at frame `at`, growing as needed.
78    pub fn mix_in(&mut self, other: &Audio, at: usize) {
79        let need = at + other.frames();
80        for (dst, src) in self.channels.iter_mut().zip(&other.channels) {
81            if dst.len() < need {
82                dst.resize(need, 0.0);
83            }
84            for (d, s) in dst[at..].iter_mut().zip(src) {
85                *d += *s;
86            }
87        }
88    }
89
90    /// Pads (with silence) or truncates to `frames`.
91    pub fn set_frames(&mut self, frames: usize) {
92        for c in &mut self.channels {
93            c.resize(frames, 0.0);
94        }
95    }
96
97    /// Reads a WAV (PCM 8/16/24/32-bit or 32-bit float).
98    pub fn read_wav(path: &Path) -> Result<Self> {
99        let err = |e: hound::Error| io_err(path, e);
100        let mut r = hound::WavReader::open(path).map_err(err)?;
101        let spec = r.spec();
102        let n = usize::from(spec.channels.max(1));
103        let interleaved: Vec<f32> = match spec.sample_format {
104            hound::SampleFormat::Float => {
105                r.samples::<f32>().collect::<Result<_, _>>().map_err(err)?
106            }
107            hound::SampleFormat::Int => {
108                let k = 1.0 / f64::from(1u32 << (spec.bits_per_sample - 1)) as f32;
109                r.samples::<i32>()
110                    .map(|s| s.map(|s| s as f32 * k))
111                    .collect::<Result<_, _>>()
112                    .map_err(err)?
113            }
114        };
115        let mut channels = vec![Vec::with_capacity(interleaved.len() / n); n];
116        for frame in interleaved.chunks_exact(n) {
117            for (c, s) in channels.iter_mut().zip(frame) {
118                c.push(*s);
119            }
120        }
121        Ok(Self {
122            rate: spec.sample_rate,
123            channels,
124        })
125    }
126
127    /// Writes a 32-bit float WAV.
128    pub fn write_wav(&self, path: &Path) -> Result<()> {
129        let err = |e: hound::Error| io_err(path, e);
130        let spec = hound::WavSpec {
131            channels: u16::try_from(self.channels.len()).map_err(|_| Error::Invalid {
132                what: "channel count",
133                value: self.channels.len().to_string(),
134            })?,
135            sample_rate: self.rate,
136            bits_per_sample: 32,
137            sample_format: hound::SampleFormat::Float,
138        };
139        let mut w = hound::WavWriter::create(path, spec).map_err(err)?;
140        for i in 0..self.frames() {
141            for c in &self.channels {
142                w.write_sample(c[i]).map_err(err)?;
143            }
144        }
145        w.finalize().map_err(err)
146    }
147}
148
149pub(crate) fn io_err(path: &Path, e: hound::Error) -> Error {
150    let source = match e {
151        hound::Error::IoError(e) => e,
152        e => std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()),
153    };
154    Error::Io {
155        path: path.to_owned(),
156        source,
157    }
158}
159
160pub fn db_to_lin(db: f64) -> f64 {
161    10f64.powf(db / 20.0)
162}
163
164pub fn lin_to_db(lin: f64) -> f64 {
165    20.0 * lin.max(1e-12).log10()
166}