浏览代码

Refactor audio output to make it more modular.

This makes the player less hard coded to portaudio, and easier to
experiment with different backends.
Paul Lietar 10 年之前
父节点
当前提交
9274a6bfb3
共有 4 个文件被更改,包括 69 次插入30 次删除
  1. 46 0
      src/audio_sink.rs
  2. 1 0
      src/lib.in.rs
  3. 6 1
      src/main.rs
  4. 16 29
      src/player.rs

+ 46 - 0
src/audio_sink.rs

@@ -0,0 +1,46 @@
+use portaudio;
+use std::io;
+
+pub trait Sink {
+    fn start(&self) -> io::Result<()>;
+    fn stop(&self) -> io::Result<()>;
+    fn write(&self, data: &[i16]) -> io::Result<()>;
+}
+
+pub struct PortAudioSink<'a>(portaudio::stream::Stream<'a, i16, i16>);
+
+impl <'a> PortAudioSink<'a> {
+    pub fn open() -> PortAudioSink<'a> {
+        portaudio::initialize().unwrap();
+
+        let stream = portaudio::stream::Stream::open_default(
+                0, 2, 44100.0,
+                portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED,
+                None
+        ).unwrap();
+
+        PortAudioSink(stream)
+    }
+}
+
+impl <'a> Sink for PortAudioSink<'a> {
+    fn start(&self) -> io::Result<()> {
+        self.0.start().unwrap();
+        Ok(())
+    }
+    fn stop(&self) -> io::Result<()> {
+        self.0.stop().unwrap();
+        Ok(())
+    }
+    fn write(&self, data: &[i16]) -> io::Result<()> {
+        self.0.write(&data).unwrap();
+        Ok(())
+    }
+}
+
+impl <'a> Drop for PortAudioSink<'a> {
+    fn drop(&mut self) {
+        portaudio::terminate().unwrap();
+    }
+}
+

+ 1 - 0
src/lib.in.rs

@@ -2,6 +2,7 @@
 mod audio_decrypt;
 mod audio_file;
 mod audio_key;
+pub mod audio_sink;
 pub mod authentication;
 mod connection;
 mod diffie_hellman;

+ 6 - 1
src/main.rs

@@ -10,6 +10,7 @@ use std::io::{stdout, Read, Write};
 use std::path::{Path, PathBuf};
 use std::thread;
 
+use librespot::audio_sink::DefaultSink;
 use librespot::authentication::Credentials;
 use librespot::discovery::DiscoveryManager;
 use librespot::player::Player;
@@ -106,10 +107,14 @@ fn main() {
     let reusable_credentials = session.login(credentials).unwrap();
     reusable_credentials.save_to_file(credentials_path);
 
-    let player = Player::new(session.clone());
+    portaudio::initialize().unwrap();
+
+    let player = Player::new(session.clone(), || DefaultSink::open());
     let spirc = SpircManager::new(session.clone(), player);
     thread::spawn(move || spirc.run());
 
+    portaudio::terminate().unwrap();
+
     loop {
         session.poll();
     }

+ 16 - 29
src/player.rs

@@ -1,14 +1,14 @@
 use eventual::{self, Async};
-use portaudio;
 use std::borrow::Cow;
 use std::sync::{mpsc, Mutex, Arc, MutexGuard};
 use std::thread;
 use std::io::{Read, Seek};
 use vorbis;
 
+use audio_decrypt::AudioDecrypt;
+use audio_sink::Sink;
 use metadata::{FileFormat, Track, TrackRef};
 use session::{Bitrate, Session};
-use audio_decrypt::AudioDecrypt;
 use util::{self, SpotifyId, Subfile};
 use spirc::PlayStatus;
 
@@ -71,7 +71,8 @@ enum PlayerCommand {
 }
 
 impl Player {
-    pub fn new(session: Session) -> Player {
+    pub fn new<S, F>(session: Session, sink_builder: F) -> Player
+        where S: Sink, F: FnOnce() -> S + Send + 'static {
         let (cmd_tx, cmd_rx) = mpsc::channel();
 
         let state = Arc::new(Mutex::new(PlayerState {
@@ -92,7 +93,7 @@ impl Player {
             observers: observers.clone(),
         };
 
-        thread::spawn(move || internal.run());
+        thread::spawn(move || internal.run(sink_builder()));
 
         Player {
             commands: cmd_tx,
@@ -154,15 +155,7 @@ fn apply_volume(volume: u16, data: &[i16]) -> Cow<[i16]> {
 }
 
 impl PlayerInternal {
-    fn run(self) {
-        portaudio::initialize().unwrap();
-
-        let stream = portaudio::stream::Stream::<i16, i16>::open_default(
-                0, 2, 44100.0,
-                portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED,
-                None
-        ).unwrap();
-
+    fn run<S: Sink>(self, sink: S) {
         let mut decoder = None;
 
         loop {
@@ -177,7 +170,7 @@ impl PlayerInternal {
                 Some(PlayerCommand::Load(track_id, play, position)) => {
                     self.update(|state| {
                         if state.status == PlayStatus::kPlayStatusPlay {
-                            stream.stop().unwrap();
+                            sink.stop().unwrap();
                         }
                         state.end_of_track = false;
                         state.status = PlayStatus::kPlayStatusPause;
@@ -221,7 +214,7 @@ impl PlayerInternal {
 
                     self.update(|state| {
                         state.status = if play {
-                            stream.start().unwrap();
+                            sink.start().unwrap();
                             PlayStatus::kPlayStatusPlay
                         } else {
                             PlayStatus::kPlayStatusPause
@@ -250,7 +243,7 @@ impl PlayerInternal {
                         true
                     });
 
-                    stream.start().unwrap();
+                    sink.start().unwrap();
                 }
                 Some(PlayerCommand::Pause) => {
                     self.update(|state| {
@@ -261,7 +254,7 @@ impl PlayerInternal {
                         true
                     });
 
-                    stream.stop().unwrap();
+                    sink.stop().unwrap();
                 }
                 Some(PlayerCommand::Volume(vol)) => {
                     self.update(|state| {
@@ -279,22 +272,20 @@ impl PlayerInternal {
                         true
                     });
 
-                    stream.stop().unwrap();
+                    sink.stop().unwrap();
                     decoder = None;
                 }
                 None => (),
             }
 
             if self.state.lock().unwrap().status == PlayStatus::kPlayStatusPlay {
-                match decoder.as_mut().unwrap().packets().next() {
+                let packet = decoder.as_mut().unwrap().packets().next();
+
+                match packet {
                     Some(Ok(packet)) => {
                         let buffer = apply_volume(self.state.lock().unwrap().volume,
                                                   &packet.data);
-                        match stream.write(&buffer) {
-                            Ok(_) => (),
-                            Err(portaudio::PaError::OutputUnderflowed) => eprintln!("Underflow"),
-                            Err(e) => panic!("PA Error {}", e),
-                        };
+                        sink.write(&buffer).unwrap();
                     }
                     Some(Err(vorbis::VorbisError::Hole)) => (),
                     Some(Err(e)) => panic!("Vorbis error {:?}", e),
@@ -305,16 +296,12 @@ impl PlayerInternal {
                             true
                         });
 
-                        stream.stop().unwrap();
+                        sink.stop().unwrap();
                         decoder = None;
                     }
                 }
             }
         }
-
-        drop(stream);
-
-        portaudio::terminate().unwrap();
     }
 
     fn update<F>(&self, f: F)