From a438981ad5a041cd0069cdcd63eb24aeb2d49c70 Mon Sep 17 00:00:00 2001 From: Warren Lo Date: Thu, 19 Mar 2026 00:31:26 +0800 Subject: [PATCH] Add audio.rs for audio playback --- src/player/audio.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/player/audio.rs diff --git a/src/player/audio.rs b/src/player/audio.rs new file mode 100644 index 0000000..976252d --- /dev/null +++ b/src/player/audio.rs @@ -0,0 +1,84 @@ +//! 音頻播放模組 + +use anyhow::Result; +use std::process::{Command, Stdio}; +use std::io::Write; +use std::sync::{Arc, Mutex}; + +#[derive(Debug, Clone)] +pub struct AudioPlayer { + process: Option, + volume: f32, + muted: bool, +} + +impl AudioPlayer { + pub fn new() -> Self { + Self { + process: None, + volume: 1.0, + muted: false, + } + } + + pub fn play(&mut self, path: &str, start_ms: u64) -> Result<()> { + self.stop()?; + + let start_sec = start_ms as f64 / 1000.0; + let volume_filter = if self.muted { + "volume=0".to_string() + } else { + format!("volume={}", self.volume) + }; + + let mut cmd = Command::new("ffplay"); + cmd.args([ + "-nodisp", + "-autoexit", + "-ss", &format!("{}", start_sec), + "-af", &volume_filter, + "-i", path, + ]) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()?; + + self.process = Some(cmd); + Ok(()) + } + + pub fn stop(&mut self) -> Result<()> { + if let Some(mut child) = self.process.take() { + let _ = child.kill(); + let _ = child.wait(); + } + Ok(()) + } + + pub fn set_volume(&mut self, volume: f32) { + self.volume = volume.clamp(0.0, 1.0); + if !self.muted { + self.restart_with_volume()?; + } + } + + pub fn toggle_mute(&mut self) -> bool { + self.muted = !self.muted; + self.muted + } + + pub fn is_muted(&self) -> bool { + self.muted + } + + fn restart_with_volume(&mut self) -> Result<()> { + Ok(()) + } +} + +impl Drop for AudioPlayer { + fn drop(&mut self) { + let _ = self.stop(); + } +}