1use std::path::Path;
4
5use ffcine_core::{Error, Result};
6
7#[derive(Debug, Clone, PartialEq, Default)]
9pub struct Audio {
10 pub rate: u32,
11 pub channels: Vec<Vec<f32>>,
12}
13
14impl Audio {
15 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 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 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 #[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 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 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 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 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 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}