1use crate::{Error, Result};
2use std::{collections::BTreeMap, str::FromStr};
3
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum AudioFormat {
6 Wav,
7 Aiff,
8 Caf,
9 Au,
10 Flac,
11 Mp3,
12 Vorbis,
14 Opus,
16 Raw,
17}
18impl FromStr for AudioFormat {
19 type Err = Error;
20 fn from_str(s: &str) -> Result<Self> {
21 match s.to_ascii_lowercase().as_str() {
22 "wav" | "wave" => Ok(Self::Wav),
23 "aiff" | "aif" | "aifc" => Ok(Self::Aiff),
24 "caf" => Ok(Self::Caf),
25 "au" | "snd" => Ok(Self::Au),
26 "flac" => Ok(Self::Flac),
27 "mp3" => Ok(Self::Mp3),
28 "ogg" | "oga" | "vorbis" => Ok(Self::Vorbis),
29 "opus" => Ok(Self::Opus),
30 "raw" | "pcm" => Ok(Self::Raw),
31 _ => Err(Error::new(
32 crate::ErrorCode::UnsupportedFormat,
33 format!("Unsupported format: {s}"),
34 )),
35 }
36 }
37}
38
39#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
40pub enum SampleFormat {
41 U8,
42 S8,
43 #[default]
44 S16,
45 S24,
46 S32,
47 F32,
48 F64,
49 Alaw,
50 Ulaw,
51}
52impl SampleFormat {
53 pub fn bytes(self) -> usize {
54 match self {
55 Self::U8 | Self::S8 | Self::Alaw | Self::Ulaw => 1,
56 Self::S16 => 2,
57 Self::S24 => 3,
58 Self::S32 | Self::F32 => 4,
59 Self::F64 => 8,
60 }
61 }
62 pub fn bits(self) -> u16 {
63 (self.bytes() * 8) as u16
64 }
65}
66
67#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
69pub enum Dither {
70 #[default]
72 None,
73 Tpdf { seed: u64 },
78}
79
80#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
81pub enum Endian {
82 #[default]
83 Little,
84 Big,
85}
86
87#[derive(Clone, Debug)]
89pub struct Limits {
90 pub max_decoded_bytes: usize,
91 pub max_duration_seconds: f64,
92 pub max_channels: usize,
93 pub min_sample_rate: u32,
94 pub max_sample_rate: u32,
95 pub max_frames: usize,
96}
97impl Default for Limits {
98 fn default() -> Self {
99 Self {
100 max_decoded_bytes: 512 * 1024 * 1024,
101 max_duration_seconds: 21600.0,
102 max_channels: 64,
103 min_sample_rate: 1000,
104 max_sample_rate: 768000,
105 max_frames: u32::MAX as usize,
106 }
107 }
108}
109impl Limits {
110 pub fn check(&self, frames: usize, channels: usize, rate: u32) -> Result<()> {
111 if !self.max_duration_seconds.is_finite() || self.max_duration_seconds < 0.0 {
112 return Err(Error::invalid(
113 "Duration limit must be finite and non-negative",
114 ));
115 }
116 if rate == 0 || rate < self.min_sample_rate || rate > self.max_sample_rate {
117 return Err(Error::limit("Sample rate exceeds decode limits"));
118 }
119 if channels == 0 || channels > self.max_channels {
120 return Err(Error::limit("Channel count exceeds decode limits"));
121 }
122 if frames > self.max_frames {
123 return Err(Error::limit("Frame count exceeds decode limits"));
124 }
125 if frames as f64 / rate as f64 > self.max_duration_seconds {
126 return Err(Error::limit("Duration exceeds decode limits"));
127 }
128 let bytes = frames
129 .checked_mul(channels)
130 .and_then(|v| v.checked_mul(4))
131 .ok_or_else(|| Error::limit("Decoded size overflow"))?;
132 if bytes > self.max_decoded_bytes {
133 return Err(Error::limit("Decoded bytes exceed decode limits"));
134 }
135 Ok(())
136 }
137 pub(crate) fn reserve_output(
142 &self,
143 output: &mut Vec<f32>,
144 next: usize,
145 channels: usize,
146 rate: u32,
147 cap: usize,
148 message: &'static str,
149 ) -> Result<()> {
150 if next <= output.capacity() {
151 return Ok(());
152 }
153 let ceiling = self
154 .max_frames
155 .min(self.max_decoded_bytes / channels / size_of::<f32>())
156 .min((self.max_duration_seconds * rate as f64).floor() as usize)
157 .min(cap);
158 let grown = output.capacity().saturating_add(output.capacity() / 2);
159 let target = next.max(grown.min(ceiling));
160 let target = if self.check(target, channels, rate).is_ok() {
163 target
164 } else {
165 next
166 };
167 output
168 .try_reserve_exact(target - output.len())
169 .map_err(|_| Error::limit(message))
170 }
171}
172
173pub type AudioMetadata = BTreeMap<String, String>;
174#[derive(Clone, Debug, Default)]
175pub struct DecodeInfo {
176 pub format: Option<AudioFormat>,
177 pub sample_format: Option<SampleFormat>,
178 pub metadata: AudioMetadata,
179 pub tags: crate::Tags,
181}
182#[derive(Clone, Debug, Default)]
183pub struct DecodeOptions {
184 pub format: Option<AudioFormat>,
185 pub limits: Limits,
186}
187#[derive(Clone, Debug)]
188pub struct EncodeOptions {
189 pub sample_format: SampleFormat,
190 pub endian: Endian,
191 pub dither: Dither,
193 pub metadata: AudioMetadata,
194 pub tags: crate::Tags,
197 pub wav_channel_mask: Option<u32>,
201 pub bitrate: u32,
203 pub compression_level: u8,
205}
206impl Default for EncodeOptions {
207 fn default() -> Self {
208 Self {
209 sample_format: SampleFormat::S16,
210 endian: Endian::Little,
211 dither: Dither::None,
212 metadata: AudioMetadata::new(),
213 tags: crate::Tags::default(),
214 bitrate: 192,
215 wav_channel_mask: None,
216 compression_level: 5,
217 }
218 }
219}
220
221#[derive(Clone, Copy, Debug)]
223pub enum TimePosition {
224 Seconds(f64),
225 Frames(i64),
226}
227impl From<f64> for TimePosition {
228 fn from(value: f64) -> Self {
229 Self::Seconds(value)
230 }
231}
232impl FromStr for TimePosition {
233 type Err = Error;
234 fn from_str(value: &str) -> Result<Self> {
235 let text = value.trim().to_ascii_lowercase();
236 let parse = |s: &str| {
237 s.trim()
238 .parse::<f64>()
239 .map_err(|_| Error::invalid("Invalid time position"))
240 };
241 let seconds = if let Some(s) = text.strip_suffix("ms") {
242 parse(s)? / 1000.0
243 } else if let Some(s) = text
244 .strip_suffix("seconds")
245 .or_else(|| text.strip_suffix("second"))
246 .or_else(|| text.strip_suffix("sec"))
247 .or_else(|| text.strip_suffix('s'))
248 {
249 parse(s)?
250 } else if text.contains(':') {
251 let parts: Vec<_> = text.split(':').collect();
252 if parts.len() > 3 {
253 return Err(Error::invalid("Too many timestamp components"));
254 }
255 let mut total = 0.0;
256 for part in parts {
257 let n = parse(part)?;
258 if n < 0.0 {
259 return Err(Error::invalid("Negative timestamp component"));
260 }
261 total = total * 60.0 + n;
262 }
263 total
264 } else {
265 parse(&text)?
266 };
267 if !seconds.is_finite() {
268 return Err(Error::invalid("Time must be finite"));
269 }
270 Ok(Self::Seconds(seconds))
271 }
272}
273impl TimePosition {
274 pub(crate) fn index(self, rate: u32, frames: usize) -> Result<usize> {
275 let n = match self {
276 Self::Frames(n) => n as f64,
277 Self::Seconds(s) => s * rate as f64,
278 };
279 if !n.is_finite() {
280 return Err(Error::invalid("Time must be finite"));
281 }
282 let n = n.round();
283 Ok((if n < 0.0 { frames as f64 + n } else { n }).clamp(0.0, frames as f64) as usize)
284 }
285 pub(crate) fn duration(self, rate: u32) -> Result<usize> {
286 let n = match self {
287 Self::Frames(n) => n as f64,
288 Self::Seconds(s) => s * rate as f64,
289 };
290 if !n.is_finite() || n < 0.0 || n > usize::MAX as f64 {
291 return Err(Error::invalid("Invalid duration"));
292 }
293 Ok(n.round() as usize)
294 }
295}