harpocrates_musik_player

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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 }