orinium_browser/platform/audio/
mod.rs1use crate::platform::io as platform_io;
4use anyhow::{Context, Result};
5use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
6use cpal::{SampleFormat, StreamConfig};
7use std::io::Cursor;
8use std::sync::{Arc, Mutex};
9use std::time::Duration;
10use symphonia::core::audio::sample::Sample;
11use symphonia::core::codecs::audio::AudioDecoderOptions;
12use symphonia::core::errors::Error;
13use symphonia::core::formats::FormatOptions;
14use symphonia::core::formats::TrackType;
15use symphonia::core::formats::probe::Hint;
16use symphonia::core::io::MediaSourceStream;
17use symphonia::core::meta::MetadataOptions;
18use symphonia::default::{get_codecs, get_probe};
19
20pub struct SoundManager {
22 samples: Arc<Mutex<Vec<f32>>>,
24 play_pos: Arc<Mutex<usize>>,
26 src_channels: usize,
28 src_sample_rate: u32,
31 stream: Option<cpal::Stream>,
33}
34
35impl SoundManager {
36 pub fn init() -> Result<Arc<Mutex<Self>>> {
38 let manager = SoundManager {
39 samples: Arc::new(Mutex::new(Vec::new())),
40 play_pos: Arc::new(Mutex::new(0)),
41 src_channels: 0,
42 src_sample_rate: 0,
43 stream: None,
44 };
45 Ok(Arc::new(Mutex::new(manager)))
46 }
47
48 fn ensure_stream(&mut self) -> Result<()> {
50 if self.stream.is_some() {
51 return Ok(());
52 }
53
54 let host = cpal::default_host();
55 let device = host
56 .default_output_device()
57 .context("No default output device available")?;
58 let supported_cfg = device
59 .default_output_config()
60 .context("Failed to get default output config")?;
61 let config: StreamConfig = supported_cfg.clone().into();
62 let sample_format = supported_cfg.sample_format();
63 let output_channels = config.channels as usize;
64
65 let samples = Arc::clone(&self.samples);
66 let play_pos = Arc::clone(&self.play_pos);
67 let src_channels = self.src_channels;
68
69 let err_fn = |err| log::error!("cpal stream error: {}", err);
70
71 let latency = Some(Duration::from_millis(100));
72
73 let stream = match sample_format {
74 SampleFormat::F32 => device.build_output_stream(
75 config,
76 move |data: &mut [f32], _| {
77 write_output_f32(data, src_channels, output_channels, &samples, &play_pos)
78 },
79 err_fn,
80 latency,
81 )?,
82 SampleFormat::I16 => device.build_output_stream(
83 config,
84 move |data: &mut [i16], _| {
85 write_output_i16(data, src_channels, output_channels, &samples, &play_pos)
86 },
87 err_fn,
88 latency,
89 )?,
90 SampleFormat::U16 => device.build_output_stream(
91 config,
92 move |data: &mut [u16], _| {
93 write_output_u16(data, src_channels, output_channels, &samples, &play_pos)
94 },
95 err_fn,
96 latency,
97 )?,
98 _ => {
99 return Err(anyhow::anyhow!(
100 "Unsupported sample format from output device"
101 ));
102 }
103 };
104
105 stream.play()?;
106 self.stream = Some(stream);
107 Ok(())
108 }
109
110 pub fn play_from_bytes(&mut self, data: &[u8]) -> Result<()> {
112 let (samples, channels, sample_rate) = decode(data)?;
113 {
115 let mut buf = self.samples.lock().unwrap_or_else(|e| e.into_inner());
116 *buf = samples;
117 }
118 {
120 let mut pos = self.play_pos.lock().unwrap_or_else(|e| e.into_inner());
121 *pos = 0;
122 }
123 self.src_channels = channels;
124 self.src_sample_rate = sample_rate;
125
126 self.ensure_stream()?;
127
128 Ok(())
129 }
130
131 pub fn play_from_file(&mut self, path: &str) -> Result<()> {
133 let data = platform_io::load_local_file(path)
134 .with_context(|| format!("Failed to read local file: {}", path))?;
135 self.play_from_bytes(&data)
136 }
137
138 pub fn play_from_local_uri(&mut self, uri: &str) -> Result<()> {
143 if uri.starts_with("resource:") {
144 let rel = uri
145 .trim_start_matches("resource:///")
146 .trim_start_matches("resource://")
147 .trim_start_matches("resource:/")
148 .trim_start_matches("resource:");
149 let rel = rel.trim_start_matches('/');
150 let data = platform_io::load_resource(rel)
151 .with_context(|| format!("Failed to load resource: {}", rel))?;
152 return self.play_from_bytes(&data);
153 }
154 if uri.starts_with("file://") {
155 let p = uri.trim_start_matches("file://");
156 let data = platform_io::load_local_file(p)
157 .with_context(|| format!("Failed to read local file from URI: {}", p))?;
158 return self.play_from_bytes(&data);
159 }
160 let data = platform_io::load_local_file(uri)
161 .with_context(|| format!("Failed to read local file: {}", uri))?;
162 self.play_from_bytes(&data)
163 }
164}
165
166fn decode(data: &[u8]) -> Result<(Vec<f32>, usize, u32)> {
168 let cursor = Cursor::new(data.to_vec());
169 let mss = MediaSourceStream::new(Box::new(cursor), Default::default());
170
171 let hint = Hint::new();
172 let fmt_opts: FormatOptions = Default::default();
173 let meta_opts: MetadataOptions = Default::default();
174 let dec_opts: AudioDecoderOptions = Default::default();
175
176 let mut format = get_probe()
177 .probe(&hint, mss, fmt_opts, meta_opts)
178 .context("Failed to probe media format")?;
179
180 let track = format
181 .default_track(TrackType::Audio)
182 .ok_or_else(|| anyhow::anyhow!("No default audio track found"))?;
183
184 let track_id = track.id;
185
186 let audio_params = track
187 .codec_params
188 .as_ref()
189 .and_then(|p| p.audio())
190 .ok_or_else(|| anyhow::anyhow!("Default track has no audio codec parameters"))?;
191
192 let mut decoder = get_codecs()
193 .make_audio_decoder(audio_params, &dec_opts)
194 .context("Failed to create audio decoder")?;
195
196 let mut samples = Vec::<f32>::new();
197 let mut channels = audio_params.channels.as_ref().map_or(1, |c| c.count());
198
199 let mut sample_rate = audio_params.sample_rate.unwrap_or(44100);
200
201 loop {
202 let packet = match format.next_packet() {
203 Ok(Some(packet)) => packet,
204 Ok(None) => break,
205 Err(err) => return Err(err).context("Failed to read media packet"),
206 };
207
208 if packet.track_id != track_id {
209 continue;
210 }
211
212 match decoder.decode(&packet) {
213 Ok(audio_buf) => {
214 channels = audio_buf.spec().channels().count();
215 sample_rate = audio_buf.spec().rate();
216
217 let mut decoded = vec![f32::MID; audio_buf.samples_interleaved()];
218 audio_buf.copy_to_slice_interleaved(&mut decoded);
219 samples.extend_from_slice(&decoded);
220 }
221 Err(Error::DecodeError(_)) => {
222 continue;
223 }
224 Err(err) => {
225 return Err(err).context("Failed to decode audio packet");
226 }
227 }
228 }
229
230 Ok((samples, channels, sample_rate))
231}
232
233fn write_output_f32(
235 output: &mut [f32],
236 src_channels: usize,
237 out_channels: usize,
238 samples: &Arc<Mutex<Vec<f32>>>,
239 pos: &Arc<Mutex<usize>>,
240) {
241 let mut p = pos.lock().unwrap();
242 let buf = samples.lock().unwrap();
243 let total_frames = if src_channels > 0 {
244 buf.len() / src_channels
245 } else {
246 0
247 };
248
249 if out_channels == 0 {
250 return;
251 }
252 let frames_to_write = output.len() / out_channels;
253
254 for frame in 0..frames_to_write {
255 if total_frames == 0 || *p >= total_frames {
256 for ch in 0..out_channels {
258 output[frame * out_channels + ch] = 0.0;
259 }
260 continue;
261 }
262 for ch in 0..out_channels {
263 let src_index = (*p * src_channels) + (ch % src_channels);
264 if src_index < buf.len() {
265 output[frame * out_channels + ch] = buf[src_index];
266 } else {
267 output[frame * out_channels + ch] = 0.0;
268 }
269 }
270 *p += 1;
271 }
272}
273
274fn write_output_i16(
276 output: &mut [i16],
277 src_channels: usize,
278 out_channels: usize,
279 samples: &Arc<Mutex<Vec<f32>>>,
280 pos: &Arc<Mutex<usize>>,
281) {
282 let mut p = pos.lock().unwrap();
283 let buf = samples.lock().unwrap();
284 let total_frames = if src_channels > 0 {
285 buf.len() / src_channels
286 } else {
287 0
288 };
289
290 if out_channels == 0 {
291 return;
292 }
293 let frames_to_write = output.len() / out_channels;
294
295 for frame in 0..frames_to_write {
296 if total_frames == 0 || *p >= total_frames {
297 for ch in 0..out_channels {
298 output[frame * out_channels + ch] = 0;
299 }
300 continue;
301 }
302 for ch in 0..out_channels {
303 let src_index = (*p * src_channels) + (ch % src_channels);
304 if src_index < buf.len() {
305 let v = buf[src_index].clamp(-1.0, 1.0);
306 output[frame * out_channels + ch] = (v * i16::MAX as f32) as i16;
307 } else {
308 output[frame * out_channels + ch] = 0;
309 }
310 }
311 *p += 1;
312 }
313}
314
315fn write_output_u16(
317 output: &mut [u16],
318 src_channels: usize,
319 out_channels: usize,
320 samples: &Arc<Mutex<Vec<f32>>>,
321 pos: &Arc<Mutex<usize>>,
322) {
323 let mut p = pos.lock().unwrap();
324 let buf = samples.lock().unwrap();
325 let total_frames = if src_channels > 0 {
326 buf.len() / src_channels
327 } else {
328 0
329 };
330
331 if out_channels == 0 {
332 return;
333 }
334 let frames_to_write = output.len() / out_channels;
335
336 for frame in 0..frames_to_write {
337 if total_frames == 0 || *p >= total_frames {
338 for ch in 0..out_channels {
339 output[frame * out_channels + ch] = 0;
340 }
341 continue;
342 }
343 for ch in 0..out_channels {
344 let src_index = (*p * src_channels) + (ch % src_channels);
345 if src_index < buf.len() {
346 let v = buf[src_index].clamp(-1.0, 1.0);
347 output[frame * out_channels + ch] = ((v * 0.5 + 0.5) * u16::MAX as f32) as u16;
348 } else {
349 output[frame * out_channels + ch] = 0;
350 }
351 }
352 *p += 1;
353 }
354}