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.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 load_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 Ok(())
127 }
128
129 pub fn play_from_bytes(&mut self, data: &[u8]) -> Result<()> {
131 self.load_from_bytes(data)?;
132
133 self.ensure_stream()?;
134
135 Ok(())
136 }
137
138 pub fn play_from_file(&mut self, path: &str) -> Result<()> {
140 let data = platform_io::load_local_file(path)
141 .with_context(|| format!("Failed to read local file: {}", path))?;
142 self.play_from_bytes(&data)
143 }
144
145 pub fn play_from_local_uri(&mut self, uri: &str) -> Result<()> {
150 if uri.starts_with("resource:") {
151 let rel = uri
152 .trim_start_matches("resource:///")
153 .trim_start_matches("resource://")
154 .trim_start_matches("resource:/")
155 .trim_start_matches("resource:");
156 let rel = rel.trim_start_matches('/');
157 let data = platform_io::load_resource(rel)
158 .with_context(|| format!("Failed to load resource: {}", rel))?;
159 return self.play_from_bytes(&data);
160 }
161 if uri.starts_with("file://") {
162 let p = uri.trim_start_matches("file://");
163 let data = platform_io::load_local_file(p)
164 .with_context(|| format!("Failed to read local file from URI: {}", p))?;
165 return self.play_from_bytes(&data);
166 }
167 let data = platform_io::load_local_file(uri)
168 .with_context(|| format!("Failed to read local file: {}", uri))?;
169 self.play_from_bytes(&data)
170 }
171
172 pub fn pause(&mut self) -> Result<()> {
174 if let Some(stream) = &self.stream {
175 stream.pause()?;
176 }
177 Ok(())
178 }
179
180 pub fn resume(&mut self) -> Result<()> {
182 if self.src_channels == 0 {
183 anyhow::bail!("Cannot resume before audio is loaded");
184 }
185 self.ensure_stream()?;
186 if let Some(stream) = &self.stream {
187 stream.play()?;
188 }
189 Ok(())
190 }
191
192 pub fn current_seconds(&self) -> f32 {
194 if self.src_sample_rate == 0 {
195 return 0.0;
196 }
197 let frame = *self.play_pos.lock().unwrap_or_else(|e| e.into_inner());
198 frame as f32 / self.src_sample_rate as f32
199 }
200
201 pub fn duration_seconds(&self) -> f32 {
203 if self.src_channels == 0 || self.src_sample_rate == 0 {
204 return 0.0;
205 }
206 let samples = self.samples.lock().unwrap_or_else(|e| e.into_inner());
207 let frames = samples.len() / self.src_channels;
208 frames as f32 / self.src_sample_rate as f32
209 }
210
211 pub fn is_finished(&self) -> bool {
213 let frame = *self.play_pos.lock().unwrap_or_else(|e| e.into_inner());
214 let samples = self.samples.lock().unwrap_or_else(|e| e.into_inner());
215 self.src_channels > 0 && frame >= samples.len() / self.src_channels
216 }
217}
218
219fn decode(data: &[u8]) -> Result<(Vec<f32>, usize, u32)> {
221 let cursor = Cursor::new(data.to_vec());
222 let mss = MediaSourceStream::new(Box::new(cursor), Default::default());
223
224 let hint = Hint::new();
225 let fmt_opts: FormatOptions = Default::default();
226 let meta_opts: MetadataOptions = Default::default();
227 let dec_opts: AudioDecoderOptions = Default::default();
228
229 let mut format = get_probe()
230 .probe(&hint, mss, fmt_opts, meta_opts)
231 .context("Failed to probe media format")?;
232
233 let track = format
234 .default_track(TrackType::Audio)
235 .ok_or_else(|| anyhow::anyhow!("No default audio track found"))?;
236
237 let track_id = track.id;
238
239 let audio_params = track
240 .codec_params
241 .as_ref()
242 .and_then(|p| p.audio())
243 .ok_or_else(|| anyhow::anyhow!("Default track has no audio codec parameters"))?;
244
245 let mut decoder = get_codecs()
246 .make_audio_decoder(audio_params, &dec_opts)
247 .context("Failed to create audio decoder")?;
248
249 let mut samples = Vec::<f32>::new();
250 let mut channels = audio_params.channels.as_ref().map_or(1, |c| c.count());
251
252 let mut sample_rate = audio_params.sample_rate.unwrap_or(44100);
253
254 loop {
255 let packet = match format.next_packet() {
256 Ok(Some(packet)) => packet,
257 Ok(None) => break,
258 Err(err) => return Err(err).context("Failed to read media packet"),
259 };
260
261 if packet.track_id != track_id {
262 continue;
263 }
264
265 match decoder.decode(&packet) {
266 Ok(audio_buf) => {
267 channels = audio_buf.spec().channels().count();
268 sample_rate = audio_buf.spec().rate();
269
270 let mut decoded = vec![f32::MID; audio_buf.samples_interleaved()];
271 audio_buf.copy_to_slice_interleaved(&mut decoded);
272 samples.extend_from_slice(&decoded);
273 }
274 Err(Error::DecodeError(_)) => {
275 continue;
276 }
277 Err(err) => {
278 return Err(err).context("Failed to decode audio packet");
279 }
280 }
281 }
282
283 Ok((samples, channels, sample_rate))
284}
285
286fn write_output_f32(
288 output: &mut [f32],
289 src_channels: usize,
290 out_channels: usize,
291 samples: &Arc<Mutex<Vec<f32>>>,
292 pos: &Arc<Mutex<usize>>,
293) {
294 let mut p = pos.lock().unwrap();
295 let buf = samples.lock().unwrap();
296 let total_frames = if src_channels > 0 {
297 buf.len() / src_channels
298 } else {
299 0
300 };
301
302 if out_channels == 0 {
303 return;
304 }
305 let frames_to_write = output.len() / out_channels;
306
307 for frame in 0..frames_to_write {
308 if total_frames == 0 || *p >= total_frames {
309 for ch in 0..out_channels {
311 output[frame * out_channels + ch] = 0.0;
312 }
313 continue;
314 }
315 for ch in 0..out_channels {
316 let src_index = (*p * src_channels) + (ch % src_channels);
317 if src_index < buf.len() {
318 output[frame * out_channels + ch] = buf[src_index];
319 } else {
320 output[frame * out_channels + ch] = 0.0;
321 }
322 }
323 *p += 1;
324 }
325}
326
327fn write_output_i16(
329 output: &mut [i16],
330 src_channels: usize,
331 out_channels: usize,
332 samples: &Arc<Mutex<Vec<f32>>>,
333 pos: &Arc<Mutex<usize>>,
334) {
335 let mut p = pos.lock().unwrap();
336 let buf = samples.lock().unwrap();
337 let total_frames = if src_channels > 0 {
338 buf.len() / src_channels
339 } else {
340 0
341 };
342
343 if out_channels == 0 {
344 return;
345 }
346 let frames_to_write = output.len() / out_channels;
347
348 for frame in 0..frames_to_write {
349 if total_frames == 0 || *p >= total_frames {
350 for ch in 0..out_channels {
351 output[frame * out_channels + ch] = 0;
352 }
353 continue;
354 }
355 for ch in 0..out_channels {
356 let src_index = (*p * src_channels) + (ch % src_channels);
357 if src_index < buf.len() {
358 let v = buf[src_index].clamp(-1.0, 1.0);
359 output[frame * out_channels + ch] = (v * i16::MAX as f32) as i16;
360 } else {
361 output[frame * out_channels + ch] = 0;
362 }
363 }
364 *p += 1;
365 }
366}
367
368fn write_output_u16(
370 output: &mut [u16],
371 src_channels: usize,
372 out_channels: usize,
373 samples: &Arc<Mutex<Vec<f32>>>,
374 pos: &Arc<Mutex<usize>>,
375) {
376 let mut p = pos.lock().unwrap();
377 let buf = samples.lock().unwrap();
378 let total_frames = if src_channels > 0 {
379 buf.len() / src_channels
380 } else {
381 0
382 };
383
384 if out_channels == 0 {
385 return;
386 }
387 let frames_to_write = output.len() / out_channels;
388
389 for frame in 0..frames_to_write {
390 if total_frames == 0 || *p >= total_frames {
391 for ch in 0..out_channels {
392 output[frame * out_channels + ch] = 0;
393 }
394 continue;
395 }
396 for ch in 0..out_channels {
397 let src_index = (*p * src_channels) + (ch % src_channels);
398 if src_index < buf.len() {
399 let v = buf[src_index].clamp(-1.0, 1.0);
400 output[frame * out_channels + ch] = ((v * 0.5 + 0.5) * u16::MAX as f32) as u16;
401 } else {
402 output[frame * out_channels + ch] = 0;
403 }
404 }
405 *p += 1;
406 }
407}