mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Refactored to have apply_volume in a specifix mixer
This commit is contained in:
parent
7be9626836
commit
347bf05dbe
5 changed files with 75 additions and 24 deletions
|
@ -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");
|
||||||
|
|
|
@ -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
14
src/mixer/mod.rs
Normal 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
46
src/mixer/softmixer.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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| {
|
||||||
|
|
Loading…
Reference in a new issue