Refactored to have apply_volume in a specifix mixer

This commit is contained in:
Daniel Romero 2017-01-20 20:39:05 +01:00
parent 7be9626836
commit 347bf05dbe
5 changed files with 75 additions and 24 deletions

View file

@ -60,6 +60,7 @@ pub mod player;
pub mod stream; pub mod stream;
pub mod util; pub mod util;
pub mod version; pub mod version;
pub mod mixer;
#[cfg(feature = "with-syntex")] include!(concat!(env!("OUT_DIR"), "/lib.rs")); #[cfg(feature = "with-syntex")] include!(concat!(env!("OUT_DIR"), "/lib.rs"));
#[cfg(not(feature = "with-syntex"))] include!("lib.in.rs"); #[cfg(not(feature = "with-syntex"))] include!("lib.in.rs");

View file

@ -18,6 +18,7 @@ use librespot::audio_backend::{self, BACKENDS};
use librespot::cache::{Cache, DefaultCache, NoCache}; use librespot::cache::{Cache, DefaultCache, NoCache};
use librespot::player::Player; use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session}; use librespot::session::{Bitrate, Config, Session};
use librespot::mixer::softmixer::SoftMixer;
use librespot::version; use librespot::version;
fn usage(program: &str, opts: &getopts::Options) -> String { fn usage(program: &str, opts: &getopts::Options) -> String {
@ -120,8 +121,10 @@ fn setup(args: &[String]) -> (Session, Player) {
matches.opt_str("password")); matches.opt_str("password"));
session.login(credentials).unwrap(); session.login(credentials).unwrap();
let mixer = SoftMixer::new();
let device_name = matches.opt_str("device"); let device_name = matches.opt_str("device");
let player = Player::new(session.clone(), move || { let player = Player::new(session.clone(), Box::new(mixer), move || {
(backend)(device_name.as_ref().map(AsRef::as_ref)) (backend)(device_name.as_ref().map(AsRef::as_ref))
}); });

14
src/mixer/mod.rs Normal file
View file

@ -0,0 +1,14 @@
use std::borrow::Cow;
pub mod softmixer;
pub trait Mixer {
fn init(&mut self);
fn inuse(&mut self);
fn release(&mut self);
fn set(&mut self, volume: u16);
fn volume(&self) -> u16;
fn apply_volume<'a>(&mut self, data: &'a [i16]) -> Cow<'a, [i16]> {
Cow::Borrowed(data)
}
}

46
src/mixer/softmixer.rs Normal file
View file

@ -0,0 +1,46 @@
use super::Mixer;
use std::borrow::Cow;
pub struct SoftMixer {
volume: u16,
}
impl SoftMixer {
pub fn new() -> SoftMixer {
SoftMixer {
volume: 0xFFFF
}
}
}
impl Mixer for SoftMixer {
fn init(&mut self) {
}
fn inuse(&mut self) {
}
fn release(&mut self) {
}
fn set(&mut self, volume: u16) {
self.volume = volume;
}
fn volume(&self) -> u16 {
self.volume
}
fn apply_volume<'a>(&mut self, data: &'a [i16]) -> Cow<'a, [i16]> {
if self.volume == 0xFFFF {
Cow::Borrowed(data)
} else {
Cow::Owned(data.iter()
.map(|&x| {
(x as i32
* self.volume as i32
/ 0xFFFF) as i16
})
.collect())
}
}
}

View file

@ -7,6 +7,7 @@ use vorbis;
use audio_decrypt::AudioDecrypt; use audio_decrypt::AudioDecrypt;
use audio_backend::Sink; use audio_backend::Sink;
use mixer::Mixer;
use metadata::{FileFormat, Track, TrackRef}; use metadata::{FileFormat, Track, TrackRef};
use session::{Bitrate, Session}; use session::{Bitrate, Session};
use util::{self, ReadSeek, SpotifyId, Subfile}; use util::{self, ReadSeek, SpotifyId, Subfile};
@ -74,8 +75,9 @@ enum PlayerCommand {
} }
impl Player { impl Player {
pub fn new<F>(session: Session, sink_builder: F) -> Player pub fn new<F, M>(session: Session, mixer: Box<M>, sink_builder: F) -> Player
where F: FnOnce() -> Box<Sink> + Send + 'static { where F: FnOnce() -> Box<Sink> + Send + 'static,
M: Mixer + Send + 'static {
let (cmd_tx, cmd_rx) = mpsc::channel(); let (cmd_tx, cmd_rx) = mpsc::channel();
let state = Arc::new(Mutex::new(PlayerState { let state = Arc::new(Mutex::new(PlayerState {
@ -83,7 +85,7 @@ impl Player {
position_ms: 0, position_ms: 0,
position_measured_at: 0, position_measured_at: 0,
update_time: util::now_ms(), update_time: util::now_ms(),
volume: 0xFFFF, volume: mixer.volume(),
track: None, track: None,
end_of_track: false, end_of_track: false,
})); }));
@ -97,7 +99,7 @@ impl Player {
observers: observers.clone(), observers: observers.clone(),
}; };
thread::spawn(move || internal.run(sink_builder())); thread::spawn(move || internal.run(sink_builder(), mixer));
Player { Player {
commands: cmd_tx, commands: cmd_tx,
@ -147,21 +149,6 @@ impl Player {
} }
} }
fn apply_volume(volume: u16, data: &[i16]) -> Cow<[i16]> {
// Fast path when volume is 100%
if volume == 0xFFFF {
Cow::Borrowed(data)
} else {
Cow::Owned(data.iter()
.map(|&x| {
(x as i32
* volume as i32
/ 0xFFFF) as i16
})
.collect())
}
}
fn find_available_alternative<'a>(session: &Session, track: &'a Track) -> Option<Cow<'a, Track>> { fn find_available_alternative<'a>(session: &Session, track: &'a Track) -> Option<Cow<'a, Track>> {
if track.available { if track.available {
Some(Cow::Borrowed(track)) Some(Cow::Borrowed(track))
@ -229,7 +216,7 @@ fn run_onstop(session: &Session) {
} }
impl PlayerInternal { impl PlayerInternal {
fn run(self, mut sink: Box<Sink>) { fn run(self, mut sink: Box<Sink>, mut mixer: Box<Mixer>) {
let mut decoder = None; let mut decoder = None;
loop { loop {
@ -344,8 +331,9 @@ impl PlayerInternal {
run_onstop(&self.session); run_onstop(&self.session);
} }
Some(PlayerCommand::Volume(vol)) => { Some(PlayerCommand::Volume(vol)) => {
mixer.set(vol);
self.update(|state| { self.update(|state| {
state.volume = vol; state.volume = mixer.volume();
true true
}); });
} }
@ -371,8 +359,7 @@ impl PlayerInternal {
match packet { match packet {
Some(Ok(packet)) => { Some(Ok(packet)) => {
let buffer = apply_volume(self.state.lock().unwrap().volume, let buffer = mixer.apply_volume(&packet.data);
&packet.data);
sink.write(&buffer).unwrap(); sink.write(&buffer).unwrap();
self.update(|state| { self.update(|state| {