Skip to main content

orinium_browser/platform/audio/
mod.rs

1//! 音声データの管理と再生を行う
2
3use 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
20/// 音声の管理を行う構造体
21pub struct SoundManager {
22    /// f32のインタリーブドサンプルバッファ
23    samples: Arc<Mutex<Vec<f32>>>,
24    /// 現在の再生位置(フレーム単位)
25    play_pos: Arc<Mutex<usize>>,
26    /// ソースのチャンネル数
27    src_channels: usize,
28    /// ソースのサンプルレート
29    // TODO: Hzを考慮したコンバーターを実装する
30    src_sample_rate: u32,
31    /// cpalのストリーム
32    stream: Option<cpal::Stream>,
33}
34
35impl SoundManager {
36    /// 初期化
37    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    /// cpalストリームを確保する
49    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    /// Decodes audio bytes and resets playback to the beginning.
111    pub fn load_from_bytes(&mut self, data: &[u8]) -> Result<()> {
112        let (samples, channels, sample_rate) = decode(data)?;
113        // replace buffer
114        {
115            let mut buf = self.samples.lock().unwrap_or_else(|e| e.into_inner());
116            *buf = samples;
117        }
118        // reset position
119        {
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    /// バイト列から音声を再生する
130    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    /// ローカルファイルから音声を再生する
139    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    /// URIから音声を再生する(resourceまたはfileスキームに対応)
146    ///
147    /// 通常の再生には `play_from_bytes` を使用してください。
148    /// これはテスト用メソッドです
149    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    /// Pauses playback while preserving the current position.
173    pub fn pause(&mut self) -> Result<()> {
174        if let Some(stream) = &self.stream {
175            stream.pause()?;
176        }
177        Ok(())
178    }
179
180    /// Resumes playback from the current position.
181    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    /// Returns the current playback position in seconds.
193    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    /// Returns the decoded audio duration in seconds.
202    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    /// Returns whether the decoded audio reached its end.
212    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
219/// 音声をデコードする
220fn 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
286/// 出力バッファに音声データを書き込む(f32)
287fn 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            // zero out remaining
310            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
327/// 出力バッファに音声データを書き込む(i16)
328fn 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
368/// 出力バッファに音声データを書き込む(u16)
369fn 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}