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.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    /// バイト列から音声を再生する
111    pub fn play_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        self.ensure_stream()?;
127
128        Ok(())
129    }
130
131    /// ローカルファイルから音声を再生する
132    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    /// URIから音声を再生する(resourceまたはfileスキームに対応)
139    ///
140    /// 通常の再生には `play_from_bytes` を使用してください。
141    /// これはテスト用メソッドです
142    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
166/// 音声をデコードする
167fn 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
233/// 出力バッファに音声データを書き込む(f32)
234fn 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            // zero out remaining
257            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
274/// 出力バッファに音声データを書き込む(i16)
275fn 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
315/// 出力バッファに音声データを書き込む(u16)
316fn 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}