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::{AudioBufferRef, Signal};
11use symphonia::core::codecs::DecoderOptions;
12use symphonia::core::formats::FormatOptions;
13use symphonia::core::io::MediaSourceStream;
14use symphonia::core::meta::MetadataOptions;
15use symphonia::core::probe::Hint;
16use symphonia::default::{get_codecs, get_probe};
17
18pub struct SoundManager {
20 samples: Arc<Mutex<Vec<f32>>>,
22 play_pos: Arc<Mutex<usize>>,
24 src_channels: usize,
26 src_sample_rate: u32,
29 stream: Option<cpal::Stream>,
31}
32
33impl SoundManager {
34 pub fn init() -> Result<Arc<Mutex<Self>>> {
36 let manager = SoundManager {
37 samples: Arc::new(Mutex::new(Vec::new())),
38 play_pos: Arc::new(Mutex::new(0)),
39 src_channels: 0,
40 src_sample_rate: 0,
41 stream: None,
42 };
43 Ok(Arc::new(Mutex::new(manager)))
44 }
45
46 fn ensure_stream(&mut self) -> Result<()> {
48 if self.stream.is_some() {
49 return Ok(());
50 }
51
52 let host = cpal::default_host();
53 let device = host
54 .default_output_device()
55 .context("No default output device available")?;
56 let supported_cfg = device
57 .default_output_config()
58 .context("Failed to get default output config")?;
59 let config: StreamConfig = supported_cfg.clone().into();
60 let sample_format = supported_cfg.sample_format();
61 let output_channels = config.channels as usize;
62
63 let samples = self.samples.clone();
64 let play_pos = self.play_pos.clone();
65 let src_channels = self.src_channels;
66
67 let err_fn = |err| log::error!("cpal stream error: {}", err);
68
69 let latency = Some(Duration::from_millis(100));
70
71 let stream = match sample_format {
72 SampleFormat::F32 => device.build_output_stream(
73 &config,
74 move |data: &mut [f32], _| {
75 write_output_f32(data, src_channels, output_channels, &samples, &play_pos)
76 },
77 err_fn,
78 latency,
79 )?,
80 SampleFormat::I16 => device.build_output_stream(
81 &config,
82 move |data: &mut [i16], _| {
83 write_output_i16(data, src_channels, output_channels, &samples, &play_pos)
84 },
85 err_fn,
86 latency,
87 )?,
88 SampleFormat::U16 => device.build_output_stream(
89 &config,
90 move |data: &mut [u16], _| {
91 write_output_u16(data, src_channels, output_channels, &samples, &play_pos)
92 },
93 err_fn,
94 latency,
95 )?,
96 _ => {
97 return Err(anyhow::anyhow!(
98 "Unsupported sample format from output device"
99 ));
100 }
101 };
102
103 stream.play()?;
104 self.stream = Some(stream);
105 Ok(())
106 }
107
108 pub fn play_from_bytes(&mut self, data: &[u8]) -> Result<()> {
110 let (samples, channels, sample_rate) = decode(data)?;
111 {
113 let mut buf = match self.samples.lock() {
114 Ok(x) => x,
115 Err(_) => todo!(),
116 };
117 *buf = samples;
118 }
119 {
121 let mut pos = match self.play_pos.lock() {
122 Ok(x) => x,
123 Err(_) => todo!(),
124 };
125 *pos = 0;
126 }
127 self.src_channels = channels;
128 self.src_sample_rate = sample_rate;
129
130 self.ensure_stream()?;
131
132 Ok(())
133 }
134
135 pub fn play_from_file(&mut self, path: &str) -> Result<()> {
137 let data = platform_io::load_local_file(path)
138 .with_context(|| format!("Failed to read local file: {}", path))?;
139 self.play_from_bytes(&data)
140 }
141
142 pub fn play_from_local_uri(&mut self, uri: &str) -> Result<()> {
147 if uri.starts_with("resource:") {
148 let rel = uri
149 .trim_start_matches("resource:///")
150 .trim_start_matches("resource://")
151 .trim_start_matches("resource:/")
152 .trim_start_matches("resource:");
153 let rel = rel.trim_start_matches('/');
154 let data = platform_io::load_resource(rel)
155 .with_context(|| format!("Failed to load resource: {}", rel))?;
156 return self.play_from_bytes(&data);
157 }
158 if uri.starts_with("file://") {
159 let p = uri.trim_start_matches("file://");
160 let data = platform_io::load_local_file(p)
161 .with_context(|| format!("Failed to read local file from URI: {}", p))?;
162 return self.play_from_bytes(&data);
163 }
164 let data = platform_io::load_local_file(uri)
165 .with_context(|| format!("Failed to read local file: {}", uri))?;
166 self.play_from_bytes(&data)
167 }
168}
169
170fn decode(data: &[u8]) -> Result<(Vec<f32>, usize, u32)> {
172 let cursor = Cursor::new(data.to_vec());
173 let mss = MediaSourceStream::new(Box::new(cursor), Default::default());
174
175 let hint = Hint::new();
176 let probed = get_probe()
177 .format(
178 &hint,
179 mss,
180 &FormatOptions::default(),
181 &MetadataOptions::default(),
182 )
183 .context("Failed to probe media format")?;
184
185 let mut format = probed.format;
186 let track = format
187 .default_track()
188 .ok_or_else(|| anyhow::anyhow!("No default audio track found"))?;
189 let codec_params = &track.codec_params;
190 let mut decoder = get_codecs()
191 .make(codec_params, &DecoderOptions::default())
192 .context("Failed to create decoder")?;
193
194 let mut samples: Vec<f32> = Vec::new();
195 let mut channels: usize = codec_params.channels.map(|c| c.count()).unwrap_or(1);
196 let mut sample_rate: u32 = codec_params.sample_rate.unwrap_or(44100);
197
198 loop {
199 match format.next_packet() {
200 Ok(packet) => match decoder.decode(&packet) {
201 Ok(audio_buf) => match audio_buf {
202 AudioBufferRef::U8(buf) => {
203 let ab = buf.as_ref();
204 channels = ab.spec().channels.count();
205 sample_rate = ab.spec().rate;
206 let frames = ab.frames();
207 for f in 0..frames {
208 for ch in 0..channels {
209 let v = ab.chan(ch)[f] as f32;
210 samples.push((v - 128.0) / 128.0);
211 }
212 }
213 }
214 AudioBufferRef::U16(buf) => {
215 let ab = buf.as_ref();
216 channels = ab.spec().channels.count();
217 sample_rate = ab.spec().rate;
218 let frames = ab.frames();
219 for f in 0..frames {
220 for ch in 0..channels {
221 let v = ab.chan(ch)[f] as f32;
222 samples.push((v - 32768.0) / 32768.0);
223 }
224 }
225 }
226 AudioBufferRef::S16(buf) => {
227 let ab = buf.as_ref();
228 channels = ab.spec().channels.count();
229 sample_rate = ab.spec().rate;
230 let frames = ab.frames();
231 for f in 0..frames {
232 for ch in 0..channels {
233 let v = ab.chan(ch)[f] as f32;
234 samples.push(v / i16::MAX as f32);
235 }
236 }
237 }
238 AudioBufferRef::F32(buf) => {
239 let ab = buf.as_ref();
240 channels = ab.spec().channels.count();
241 sample_rate = ab.spec().rate;
242 let frames = ab.frames();
243 for f in 0..frames {
244 for ch in 0..channels {
245 let v = ab.chan(ch)[f];
246 samples.push(v);
247 }
248 }
249 }
250 AudioBufferRef::F64(buf) => {
251 let ab = buf.as_ref();
252 channels = ab.spec().channels.count();
253 sample_rate = ab.spec().rate;
254 let frames = ab.frames();
255 for f in 0..frames {
256 for ch in 0..channels {
257 let v = ab.chan(ch)[f];
258 samples.push(v as f32);
259 }
260 }
261 }
262 _ => {
263 }
265 },
266 Err(_) => { }
267 },
268 Err(_) => break,
269 }
270 }
271
272 Ok((samples, channels, sample_rate))
273}
274
275fn write_output_f32(
277 output: &mut [f32],
278 src_channels: usize,
279 out_channels: usize,
280 samples: &Arc<Mutex<Vec<f32>>>,
281 pos: &Arc<Mutex<usize>>,
282) {
283 let mut p = pos.lock().unwrap();
284 let buf = samples.lock().unwrap();
285 let total_frames = if src_channels > 0 {
286 buf.len() / src_channels
287 } else {
288 0
289 };
290
291 if out_channels == 0 {
292 return;
293 }
294 let frames_to_write = output.len() / out_channels;
295
296 for frame in 0..frames_to_write {
297 if total_frames == 0 || *p >= total_frames {
298 for ch in 0..out_channels {
300 output[frame * out_channels + ch] = 0.0;
301 }
302 continue;
303 }
304 for ch in 0..out_channels {
305 let src_index = (*p * src_channels) + (ch % src_channels);
306 if src_index < buf.len() {
307 output[frame * out_channels + ch] = buf[src_index];
308 } else {
309 output[frame * out_channels + ch] = 0.0;
310 }
311 }
312 *p += 1;
313 }
314}
315
316fn write_output_i16(
318 output: &mut [i16],
319 src_channels: usize,
320 out_channels: usize,
321 samples: &Arc<Mutex<Vec<f32>>>,
322 pos: &Arc<Mutex<usize>>,
323) {
324 let mut p = pos.lock().unwrap();
325 let buf = samples.lock().unwrap();
326 let total_frames = if src_channels > 0 {
327 buf.len() / src_channels
328 } else {
329 0
330 };
331
332 if out_channels == 0 {
333 return;
334 }
335 let frames_to_write = output.len() / out_channels;
336
337 for frame in 0..frames_to_write {
338 if total_frames == 0 || *p >= total_frames {
339 for ch in 0..out_channels {
340 output[frame * out_channels + ch] = 0;
341 }
342 continue;
343 }
344 for ch in 0..out_channels {
345 let src_index = (*p * src_channels) + (ch % src_channels);
346 if src_index < buf.len() {
347 let v = buf[src_index].clamp(-1.0, 1.0);
348 output[frame * out_channels + ch] = (v * i16::MAX as f32) as i16;
349 } else {
350 output[frame * out_channels + ch] = 0;
351 }
352 }
353 *p += 1;
354 }
355}
356
357fn write_output_u16(
359 output: &mut [u16],
360 src_channels: usize,
361 out_channels: usize,
362 samples: &Arc<Mutex<Vec<f32>>>,
363 pos: &Arc<Mutex<usize>>,
364) {
365 let mut p = pos.lock().unwrap();
366 let buf = samples.lock().unwrap();
367 let total_frames = if src_channels > 0 {
368 buf.len() / src_channels
369 } else {
370 0
371 };
372
373 if out_channels == 0 {
374 return;
375 }
376 let frames_to_write = output.len() / out_channels;
377
378 for frame in 0..frames_to_write {
379 if total_frames == 0 || *p >= total_frames {
380 for ch in 0..out_channels {
381 output[frame * out_channels + ch] = 0;
382 }
383 continue;
384 }
385 for ch in 0..out_channels {
386 let src_index = (*p * src_channels) + (ch % src_channels);
387 if src_index < buf.len() {
388 let v = buf[src_index].clamp(-1.0, 1.0);
389 output[frame * out_channels + ch] = ((v * 0.5 + 0.5) * u16::MAX as f32) as u16;
390 } else {
391 output[frame * out_channels + ch] = 0;
392 }
393 }
394 *p += 1;
395 }
396}