Skip to main content

raw_player/
audio_player.rs

1use std::collections::VecDeque;
2use std::sync::Mutex;
3
4use crate::audio_format::AudioFormat;
5use crate::audio_stream::AudioStream;
6use crate::error::{Error, Result};
7use crate::ffi;
8
9struct AudioChunk {
10    pts_us: i64,
11    sample_rate: i32,
12    channels: i32,
13    format: AudioFormat,
14    data: Vec<u8>,
15}
16
17#[derive(Debug, Clone)]
18pub struct AudioPlayerStats {
19    pub audio_queue_size: usize,
20    pub audio_buffer_ms: f64,
21    pub chunks_played: i64,
22    pub audio_clock_us: i64,
23    pub sample_rate: i32,
24    pub channels: i32,
25    pub is_float: bool,
26    pub total_samples_enqueued: i64,
27    pub total_samples_played: i64,
28    pub elapsed_time_ms: f64,
29    pub audio_bitrate_kbps: f64,
30}
31
32struct AudioPlayerInner {
33    stream: Option<AudioStream>,
34    audio_queue: VecDeque<AudioChunk>,
35    sample_rate: i32,
36    channels: i32,
37    is_float: bool,
38    samples_written: i64,
39    first_pts_us: i64,
40    audio_started: bool,
41    playing: bool,
42    has_played: bool,
43    volume: f32,
44    chunks_played: i64,
45    total_samples_enqueued: i64,
46    play_start_time_ns: u64,
47}
48
49pub struct AudioPlayer {
50    inner: Mutex<AudioPlayerInner>,
51}
52
53impl AudioPlayer {
54    pub fn new() -> Self {
55        Self {
56            inner: Mutex::new(AudioPlayerInner {
57                stream: None,
58                audio_queue: VecDeque::new(),
59                sample_rate: 0,
60                channels: 0,
61                is_float: false,
62                samples_written: 0,
63                first_pts_us: 0,
64                audio_started: false,
65                playing: false,
66                has_played: false,
67                volume: 1.0,
68                chunks_played: 0,
69                total_samples_enqueued: 0,
70                play_start_time_ns: 0,
71            }),
72        }
73    }
74
75    /// PCM データをキューに追加する。
76    pub fn enqueue_audio(
77        &self,
78        data: &[u8],
79        pts_us: i64,
80        sample_rate: i32,
81        channels: i32,
82        format: AudioFormat,
83    ) -> Result<()> {
84        if data.is_empty() {
85            return Err(Error::invalid_argument("data is empty"));
86        }
87        if sample_rate <= 0 {
88            return Err(Error::invalid_argument("sample_rate must be positive"));
89        }
90        if channels <= 0 {
91            return Err(Error::invalid_argument("channels must be positive"));
92        }
93
94        let sample_size = format.sample_size();
95        let frame_size = channels as usize * sample_size;
96        if !data.len().is_multiple_of(frame_size) {
97            return Err(Error::invalid_argument(
98                "data size is not aligned to frame size",
99            ));
100        }
101
102        let num_samples = data.len() as i64 / (channels as i64 * sample_size as i64);
103
104        let mut inner = self.inner.lock().unwrap();
105
106        if inner.has_played && !inner.playing {
107            return Err(Error::NotPlaying);
108        }
109
110        inner.total_samples_enqueued += num_samples;
111        inner.audio_queue.push_back(AudioChunk {
112            pts_us,
113            sample_rate,
114            channels,
115            format,
116            data: data.to_vec(),
117        });
118
119        Ok(())
120    }
121
122    /// 再生を開始する。
123    pub fn play(&self) -> Result<()> {
124        let mut inner = self.inner.lock().unwrap();
125        if !inner.playing {
126            inner.playing = true;
127            inner.has_played = true;
128            if inner.play_start_time_ns == 0 {
129                inner.play_start_time_ns = unsafe { ffi::SDL_GetTicksNS() };
130            }
131            Self::process_audio_queue(&mut inner)?;
132            if let Some(ref mut stream) = inner.stream {
133                stream.resume()?;
134            }
135        }
136        Ok(())
137    }
138
139    /// 再生を一時停止する。
140    pub fn pause(&self) -> Result<()> {
141        let mut inner = self.inner.lock().unwrap();
142        if inner.playing {
143            inner.playing = false;
144            if let Some(ref mut stream) = inner.stream {
145                stream.pause()?;
146            }
147        }
148        Ok(())
149    }
150
151    /// 再生を停止してキューをクリアする。
152    pub fn stop(&self) -> Result<()> {
153        let mut inner = self.inner.lock().unwrap();
154        inner.playing = false;
155        inner.has_played = false;
156        inner.audio_queue.clear();
157        if let Some(ref mut stream) = inner.stream {
158            stream.pause()?;
159            stream.clear()?;
160        }
161        inner.samples_written = 0;
162        inner.audio_started = false;
163        inner.total_samples_enqueued = 0;
164        inner.play_start_time_ns = 0;
165        inner.chunks_played = 0;
166        Ok(())
167    }
168
169    pub fn is_playing(&self) -> bool {
170        self.inner.lock().unwrap().playing
171    }
172
173    pub fn volume(&self) -> f32 {
174        self.inner.lock().unwrap().volume
175    }
176
177    pub fn set_volume(&self, volume: f32) -> Result<()> {
178        let mut inner = self.inner.lock().unwrap();
179        let clamped = volume.clamp(0.0, 1.0);
180        inner.volume = clamped;
181        if let Some(ref mut stream) = inner.stream {
182            stream.set_gain(clamped)?;
183        }
184        Ok(())
185    }
186
187    /// 現在の音声再生位置をマイクロ秒で返す。
188    pub fn audio_clock_us(&self) -> i64 {
189        let inner = self.inner.lock().unwrap();
190        Self::get_audio_clock_us(&inner)
191    }
192
193    /// 音声再生が開始されたかを返す。
194    pub fn is_started(&self) -> bool {
195        self.inner.lock().unwrap().audio_started
196    }
197
198    /// キューに溜まったチャンクをストリームにフラッシュする (再生中のみ)。
199    pub fn process(&self) -> Result<()> {
200        let mut inner = self.inner.lock().unwrap();
201        if inner.playing {
202            Self::process_audio_queue(&mut inner)?;
203        }
204        Ok(())
205    }
206
207    /// 音声キューのバッファ量をミリ秒で返す。
208    pub fn audio_queue_ms(&self) -> f64 {
209        let inner = self.inner.lock().unwrap();
210        if inner.sample_rate > 0 {
211            let sample_size: i64 = if inner.is_float { 4 } else { 2 };
212            let bytes_per_frame = inner.channels as i64 * sample_size;
213            let queued_bytes: i64 = inner.audio_queue.iter().map(|c| c.data.len() as i64).sum();
214            let stream_queued = inner
215                .stream
216                .as_ref()
217                .map(|s| s.queued_bytes() as i64)
218                .unwrap_or(0);
219            let total = queued_bytes + stream_queued;
220            if bytes_per_frame > 0 {
221                let samples = total / bytes_per_frame;
222                samples as f64 * 1000.0 / inner.sample_rate as f64
223            } else {
224                0.0
225            }
226        } else {
227            0.0
228        }
229    }
230
231    pub fn stats(&self) -> AudioPlayerStats {
232        let inner = self.inner.lock().unwrap();
233
234        let clock_us = Self::get_audio_clock_us(&inner);
235
236        let buffer_ms = if inner.sample_rate > 0 {
237            let sample_size = if inner.is_float { 4 } else { 2 };
238            let bytes_per_frame = inner.channels as i64 * sample_size;
239            let total_queued_bytes: i64 =
240                inner.audio_queue.iter().map(|c| c.data.len() as i64).sum();
241            let stream_queued = inner
242                .stream
243                .as_ref()
244                .map(|s| s.queued_bytes() as i64)
245                .unwrap_or(0);
246            let total_bytes = total_queued_bytes + stream_queued;
247            if bytes_per_frame > 0 {
248                let samples = total_bytes / bytes_per_frame;
249                samples as f64 * 1000.0 / inner.sample_rate as f64
250            } else {
251                0.0
252            }
253        } else {
254            0.0
255        };
256
257        let elapsed_ms = if inner.has_played {
258            let now = unsafe { ffi::SDL_GetTicksNS() };
259            (now - inner.play_start_time_ns) as f64 / 1_000_000.0
260        } else {
261            0.0
262        };
263
264        let sample_size = if inner.is_float { 4 } else { 2 };
265        let bitrate_kbps = if inner.sample_rate > 0 {
266            inner.sample_rate as f64 * inner.channels as f64 * sample_size as f64 * 8.0 / 1000.0
267        } else {
268            0.0
269        };
270
271        let queued_samples = inner
272            .stream
273            .as_ref()
274            .map(|s| {
275                let bytes = s.queued_bytes() as i64;
276                let bytes_per_frame = inner.channels as i64 * sample_size;
277                if bytes_per_frame > 0 {
278                    bytes / bytes_per_frame
279                } else {
280                    0
281                }
282            })
283            .unwrap_or(0);
284        let played_samples = (inner.samples_written - queued_samples).max(0);
285
286        AudioPlayerStats {
287            audio_queue_size: inner.audio_queue.len(),
288            audio_buffer_ms: buffer_ms,
289            chunks_played: inner.chunks_played,
290            audio_clock_us: clock_us,
291            sample_rate: inner.sample_rate,
292            channels: inner.channels,
293            is_float: inner.is_float,
294            total_samples_enqueued: inner.total_samples_enqueued,
295            total_samples_played: played_samples,
296            elapsed_time_ms: elapsed_ms,
297            audio_bitrate_kbps: bitrate_kbps,
298        }
299    }
300
301    fn get_audio_clock_us(inner: &AudioPlayerInner) -> i64 {
302        if !inner.audio_started || inner.stream.is_none() {
303            return 0;
304        }
305
306        let sample_size: i64 = if inner.is_float { 4 } else { 2 };
307        let bytes_per_frame = inner.channels as i64 * sample_size;
308
309        let queued_bytes = inner
310            .stream
311            .as_ref()
312            .map(|s| s.queued_bytes() as i64)
313            .unwrap_or(0);
314
315        let queued_samples = if bytes_per_frame > 0 {
316            queued_bytes / bytes_per_frame
317        } else {
318            0
319        };
320
321        let played_samples = (inner.samples_written - queued_samples).max(0);
322
323        if inner.sample_rate > 0 {
324            let played_us = played_samples * 1_000_000 / inner.sample_rate as i64;
325            inner.first_pts_us + played_us
326        } else {
327            0
328        }
329    }
330
331    fn process_audio_queue(inner: &mut AudioPlayerInner) -> Result<()> {
332        while let Some(chunk) = inner.audio_queue.pop_front() {
333            let format_changed = inner.stream.is_some()
334                && (chunk.sample_rate != inner.sample_rate
335                    || chunk.channels != inner.channels
336                    || chunk.format.is_float() != inner.is_float);
337
338            if inner.stream.is_none() || format_changed {
339                inner.stream = None;
340                let mut stream =
341                    match AudioStream::open(chunk.sample_rate, chunk.channels, chunk.format) {
342                        Ok(s) => s,
343                        Err(e) => {
344                            inner.audio_queue.push_front(chunk);
345                            return Err(e);
346                        }
347                    };
348                if let Err(e) = stream.set_gain(inner.volume) {
349                    inner.audio_queue.push_front(chunk);
350                    return Err(e);
351                }
352                if inner.playing
353                    && let Err(e) = stream.resume()
354                {
355                    inner.audio_queue.push_front(chunk);
356                    return Err(e);
357                }
358                inner.stream = Some(stream);
359                inner.sample_rate = chunk.sample_rate;
360                inner.channels = chunk.channels;
361                inner.is_float = chunk.format.is_float();
362                inner.samples_written = 0;
363                // フォーマット変更は新しい再生系列なのでクロック基準を更新する
364                inner.first_pts_us = chunk.pts_us;
365                inner.audio_started = true;
366            }
367
368            if !inner.audio_started {
369                inner.first_pts_us = chunk.pts_us;
370                inner.audio_started = true;
371            }
372
373            let sample_size = if inner.is_float { 4 } else { 2 };
374            let bytes_per_frame = inner.channels as i64 * sample_size;
375            let num_samples = if bytes_per_frame > 0 {
376                chunk.data.len() as i64 / bytes_per_frame
377            } else {
378                0
379            };
380
381            if let Some(ref mut stream) = inner.stream
382                && let Err(e) = stream.put_data(&chunk.data)
383            {
384                inner.audio_queue.push_front(chunk);
385                return Err(e);
386            }
387
388            inner.samples_written += num_samples;
389            inner.chunks_played += 1;
390        }
391        Ok(())
392    }
393}
394
395impl Default for AudioPlayer {
396    fn default() -> Self {
397        Self::new()
398    }
399}
400
401#[cfg(test)]
402mod tests {
403    use std::collections::VecDeque;
404
405    /// `process_audio_queue` がストリームエラー時に行う `pop_front` → 失敗 → `push_front` と同じ順序不変条件。
406    /// SDL を使わず退行を検知する最小モデル。
407    #[test]
408    fn requeue_after_pop_restores_queue_order() {
409        let mut q = VecDeque::from([1u32, 2, 3]);
410        let c = q.pop_front().unwrap();
411        assert_eq!(c, 1);
412        q.push_front(c);
413        assert_eq!(q.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
414    }
415}