mittens_engine/engine/ecs/system/
audio_sample_format_convert.rs1use super::audio_decode::DecodedAudio;
8
9#[derive(Debug, Clone, Copy)]
10pub struct PlaybackFormat {
11 pub sample_rate: u32,
12 pub channels: u16,
13}
14
15#[derive(Debug, Clone)]
17pub struct ConvertedAudio {
18 pub samples: std::sync::Arc<Vec<f32>>,
19 pub channels: u16,
20 pub sample_rate: u32,
21}
22
23#[derive(Debug, Clone)]
24pub enum ConvertError {
25 InvalidTarget(String),
26 EmptyInput,
27}
28
29impl std::fmt::Display for ConvertError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 ConvertError::InvalidTarget(s) => write!(f, "invalid target: {s}"),
33 ConvertError::EmptyInput => write!(f, "empty input"),
34 }
35 }
36}
37
38impl std::error::Error for ConvertError {}
39
40pub fn convert_sample_format(
43 decoded: DecodedAudio,
44 target: PlaybackFormat,
45) -> Result<ConvertedAudio, ConvertError> {
46 if target.sample_rate == 0 || target.channels == 0 {
47 return Err(ConvertError::InvalidTarget(format!("{target:?}")));
48 }
49 if decoded.samples.is_empty() {
50 return Err(ConvertError::EmptyInput);
51 }
52
53 let after_channels = remix_channels(&decoded.samples, decoded.channels, target.channels);
54 let after_resample = if decoded.sample_rate == target.sample_rate {
55 after_channels
56 } else {
57 resample_linear(
58 &after_channels,
59 target.channels,
60 decoded.sample_rate,
61 target.sample_rate,
62 )
63 };
64
65 Ok(ConvertedAudio {
66 samples: std::sync::Arc::new(after_resample),
67 channels: target.channels,
68 sample_rate: target.sample_rate,
69 })
70}
71
72fn remix_channels(samples: &[f32], src: u16, dst: u16) -> Vec<f32> {
73 if src == dst {
74 return samples.to_vec();
75 }
76 let src_n = src as usize;
77 let dst_n = dst as usize;
78 let frames = samples.len() / src_n.max(1);
79
80 if dst == 1 {
81 let mut out = Vec::with_capacity(frames);
83 for f in 0..frames {
84 let base = f * src_n;
85 let mut sum = 0.0f32;
86 for c in 0..src_n {
87 sum += samples[base + c];
88 }
89 out.push(sum / src_n as f32);
90 }
91 out
92 } else if src == 1 {
93 let mut out = Vec::with_capacity(frames * dst_n);
95 for f in 0..frames {
96 let s = samples[f];
97 for _ in 0..dst_n {
98 out.push(s);
99 }
100 }
101 out
102 } else {
103 let copy_n = src_n.min(dst_n);
107 let mut out = Vec::with_capacity(frames * dst_n);
108 for f in 0..frames {
109 let base = f * src_n;
110 for c in 0..copy_n {
111 out.push(samples[base + c]);
112 }
113 for _ in copy_n..dst_n {
114 out.push(0.0);
115 }
116 }
117 out
118 }
119}
120
121fn resample_linear(samples: &[f32], channels: u16, src_sr: u32, dst_sr: u32) -> Vec<f32> {
125 if src_sr == dst_sr {
126 return samples.to_vec();
127 }
128 let ch = channels as usize;
129 let in_frames = samples.len() / ch.max(1);
130 if in_frames < 2 {
131 return samples.to_vec();
132 }
133
134 let ratio = dst_sr as f64 / src_sr as f64;
135 let out_frames = ((in_frames as f64) * ratio).round() as usize;
136 let mut out = Vec::with_capacity(out_frames * ch);
137
138 for f in 0..out_frames {
139 let src_pos = f as f64 / ratio;
140 let i0 = src_pos.floor() as usize;
141 let i1 = (i0 + 1).min(in_frames - 1);
142 let t = (src_pos - i0 as f64) as f32;
143 for c in 0..ch {
144 let s0 = samples[i0 * ch + c];
145 let s1 = samples[i1 * ch + c];
146 out.push(s0 + (s1 - s0) * t);
147 }
148 }
149 out
150}