audio.rs (3688B)
1 use crate::setting::AudioSettings; 2 use rodio::Device; 3 use rodio::cpal::traits::{DeviceTrait, HostTrait}; 4 use rodio::stream::{DeviceSinkBuilder, MixerDeviceSink}; 5 6 pub struct AudioEngine { 7 player: rodio::Player, 8 stream_handle: rodio::MixerDeviceSink, 9 } 10 pub trait AudioPlayer { 11 fn new(audio_settings: &AudioSettings) -> Result<AudioEngine, Box<dyn std::error::Error>>; 12 fn append_a_track(&mut self, file: &str) -> Result<(), Box<dyn std::error::Error>>; 13 fn play(&mut self); 14 fn pause(&mut self); 15 fn stop(&mut self); 16 fn is_paused(&self) -> bool; 17 fn duration(&self) -> std::time::Duration; 18 fn try_seek(&mut self, position: std::time::Duration) 19 -> Result<(), Box<dyn std::error::Error>>; 20 fn is_empty(&self) -> bool; 21 fn volume(&self) -> f32; 22 fn set_volume(&mut self, volume: f32); 23 fn append_tracks(&mut self, files: &[&str]) -> Result<(), Box<dyn std::error::Error>>; 24 } 25 26 impl AudioPlayer for AudioEngine { 27 fn new(audio_settings: &AudioSettings) -> Result<Self, Box<dyn std::error::Error>> { 28 let stream_handle = DeviceManager::open_sink(&audio_settings.output_device())?; 29 let player = rodio::Player::connect_new(stream_handle.mixer()); 30 Ok(Self { 31 player, 32 stream_handle, 33 }) 34 } 35 fn append_a_track(&mut self, file: &str) -> Result<(), Box<dyn std::error::Error>> { 36 let file = std::fs::File::open(file)?; 37 self.player.append(rodio::Decoder::try_from(file)?); 38 Ok(()) 39 } 40 fn play(&mut self) { 41 self.player.play(); 42 } 43 fn pause(&mut self) { 44 self.player.pause(); 45 } 46 fn stop(&mut self) { 47 self.player.stop(); 48 } 49 fn is_paused(&self) -> bool { 50 self.player.is_paused() 51 } 52 fn try_seek( 53 &mut self, 54 position: std::time::Duration, 55 ) -> Result<(), Box<dyn std::error::Error>> { 56 self.player.try_seek(position)?; 57 Ok(()) 58 } 59 fn duration(&self) -> std::time::Duration { 60 self.player.get_pos() 61 } 62 fn is_empty(&self) -> bool { 63 self.player.empty() 64 } 65 fn volume(&self) -> f32 { 66 self.player.volume() 67 } 68 fn set_volume(&mut self, volume: f32) { 69 self.player.set_volume(volume); 70 } 71 fn append_tracks(&mut self, files: &[&str]) -> Result<(), Box<dyn std::error::Error>> { 72 for file in files { 73 self.append_a_track(file)?; 74 } 75 Ok(()) 76 } 77 } 78 79 pub struct DeviceManager; 80 81 impl DeviceManager { 82 pub fn list_device_names() -> Vec<String> { 83 let host = rodio::cpal::default_host(); 84 let Ok(devices) = host.output_devices() else { 85 return Vec::new(); 86 }; 87 88 devices.filter_map(|d| d.name().ok()).collect() 89 } 90 91 pub fn find_device_by_name(name: &str) -> Option<Device> { 92 if name.trim().is_empty() { 93 return None; 94 } 95 96 let host = rodio::cpal::default_host(); 97 let Ok(mut devices) = host.output_devices() else { 98 return None; 99 }; 100 101 devices.find(|d| d.name().map(|n| n == name).unwrap_or(false)) 102 } 103 104 pub fn open_sink(device_name: &str) -> Result<MixerDeviceSink, Box<dyn std::error::Error>> { 105 if let Some(device) = Self::find_device_by_name(device_name) { 106 if let Ok(builder) = DeviceSinkBuilder::from_device(device) { 107 if let Ok(sink) = builder.open_stream() { 108 return Ok(sink); 109 } 110 } 111 } 112 113 let sink = DeviceSinkBuilder::open_default_sink()?; 114 Ok(sink) 115 } 116 }