Run rustfmt against whole project

This commit is contained in:
Sasha Hilton 2018-02-23 10:13:05 +01:00
parent ff1f7e1d58
commit 6d24d8e1eb
20 changed files with 428 additions and 310 deletions

View file

@ -7,8 +7,9 @@ use std::ops::Add;
use core::audio_key::AudioKey;
const AUDIO_AESIV: &'static [u8] = &[0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8,
0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93];
const AUDIO_AESIV: &'static [u8] = &[
0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93
];
pub struct AudioDecrypt<T: io::Read> {
cipher: Box<SynchronousStreamCipher + 'static>,

View file

@ -1,11 +1,11 @@
use bit_set::BitSet;
use byteorder::{ByteOrder, BigEndian, WriteBytesExt};
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use futures::Stream;
use futures::sync::{oneshot, mpsc};
use futures::{Poll, Async, Future};
use futures::sync::{mpsc, oneshot};
use futures::{Async, Future, Poll};
use std::cmp::min;
use std::fs;
use std::io::{self, Read, Write, Seek, SeekFrom};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::sync::{Arc, Condvar, Mutex};
use tempfile::NamedTempFile;
@ -71,7 +71,12 @@ impl AudioFileOpenStreaming {
let (seek_tx, seek_rx) = mpsc::unbounded();
let fetcher = AudioFileFetch::new(
self.session.clone(), shared.clone(), data_rx, write_file, seek_rx, complete_tx
self.session.clone(),
shared.clone(),
data_rx,
write_file,
seek_rx,
complete_tx,
);
self.session.spawn(move |_| fetcher);
@ -148,14 +153,16 @@ impl AudioFile {
let session_ = session.clone();
session.spawn(move |_| {
complete_rx.map(move |mut file| {
complete_rx
.map(move |mut file| {
if let Some(cache) = session_.cache() {
cache.save_file(file_id, &mut file);
debug!("File {} complete, saving to cache", file_id);
} else {
debug!("File {} complete", file_id);
}
}).or_else(|oneshot::Canceled| Ok(()))
})
.or_else(|oneshot::Canceled| Ok(()))
});
AudioFileOpen::Streaming(open)
@ -200,11 +207,14 @@ struct AudioFileFetch {
}
impl AudioFileFetch {
fn new(session: Session, shared: Arc<AudioFileShared>,
data_rx: ChannelData, output: NamedTempFile,
fn new(
session: Session,
shared: Arc<AudioFileShared>,
data_rx: ChannelData,
output: NamedTempFile,
seek_rx: mpsc::UnboundedReceiver<u64>,
complete_tx: oneshot::Sender<NamedTempFile>) -> AudioFileFetch
{
complete_tx: oneshot::Sender<NamedTempFile>,
) -> AudioFileFetch {
AudioFileFetch {
session: session,
shared: shared,
@ -233,8 +243,11 @@ impl AudioFileFetch {
let offset = self.index * CHUNK_SIZE;
self.output.as_mut().unwrap()
.seek(SeekFrom::Start(offset as u64)).unwrap();
self.output
.as_mut()
.unwrap()
.seek(SeekFrom::Start(offset as u64))
.unwrap();
let (_headers, data) = request_chunk(&self.session, self.shared.file_id, self.index).split();
self.data_rx = data;
@ -275,13 +288,20 @@ impl Future for AudioFileFetch {
Ok(Async::Ready(Some(data))) => {
progress = true;
self.output.as_mut().unwrap()
.write_all(data.as_ref()).unwrap();
self.output
.as_mut()
.unwrap()
.write_all(data.as_ref())
.unwrap();
}
Ok(Async::Ready(None)) => {
progress = true;
trace!("chunk {} / {} complete", self.index, self.shared.chunk_count);
trace!(
"chunk {} / {} complete",
self.index,
self.shared.chunk_count
);
let full = {
let mut bitmap = self.shared.bitmap.lock().unwrap();
@ -303,7 +323,7 @@ impl Future for AudioFileFetch {
Err(ChannelError) => {
warn!("error from channel");
return Ok(Async::Ready(()));
},
}
}
if !progress {

View file

@ -10,8 +10,9 @@ pub struct VorbisDecoder<R: Read + Seek>(OggStreamReader<R>);
pub struct VorbisPacket(Vec<i16>);
pub struct VorbisError(lewton::VorbisError);
impl <R> VorbisDecoder<R>
where R: Read + Seek
impl<R> VorbisDecoder<R>
where
R: Read + Seek,
{
pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
Ok(VorbisDecoder(OggStreamReader::new(input)?))

View file

@ -1,11 +1,13 @@
#[macro_use] extern crate log;
#[macro_use] extern crate futures;
#[macro_use]
extern crate futures;
#[macro_use]
extern crate log;
extern crate bit_set;
extern crate byteorder;
extern crate crypto;
extern crate num_traits;
extern crate num_bigint;
extern crate num_traits;
extern crate tempfile;
extern crate librespot_core as core;
@ -22,6 +24,6 @@ pub use fetch::{AudioFile, AudioFileOpen};
pub use decrypt::AudioDecrypt;
#[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))]
pub use lewton_decoder::{VorbisDecoder, VorbisPacket, VorbisError};
pub use lewton_decoder::{VorbisDecoder, VorbisError, VorbisPacket};
#[cfg(any(feature = "with-tremor", feature = "with-vorbis"))]
pub use libvorbis_decoder::{VorbisDecoder, VorbisPacket, VorbisError};
pub use libvorbis_decoder::{VorbisDecoder, VorbisError, VorbisPacket};

View file

@ -1,5 +1,7 @@
#[cfg(not(feature = "with-tremor"))] extern crate vorbis;
#[cfg(feature = "with-tremor")] extern crate tremor as vorbis;
#[cfg(feature = "with-tremor")]
extern crate tremor as vorbis;
#[cfg(not(feature = "with-tremor"))]
extern crate vorbis;
use std::io::{Read, Seek};
use std::fmt;
@ -9,8 +11,9 @@ pub struct VorbisDecoder<R: Read + Seek>(vorbis::Decoder<R>);
pub struct VorbisPacket(vorbis::Packet);
pub struct VorbisError(vorbis::VorbisError);
impl <R> VorbisDecoder<R>
where R: Read + Seek
impl<R> VorbisDecoder<R>
where
R: Read + Seek,
{
pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
Ok(VorbisDecoder(vorbis::Decoder::new(input)?))

View file

@ -20,7 +20,7 @@ fn main() {
let session_config = SessionConfig::default();
let player_config = PlayerConfig::default();
let args : Vec<_> = env::args().collect();
let args: Vec<_> = env::args().collect();
if args.len() != 4 {
println!("Usage: {} USERNAME PASSWORD TRACK", args[0]);
}
@ -33,9 +33,12 @@ fn main() {
let backend = audio_backend::find(None).unwrap();
println!("Connecting ..");
let session = core.run(Session::connect(session_config, credentials, None, handle)).unwrap();
let session = core.run(Session::connect(session_config, credentials, None, handle))
.unwrap();
let player = Player::new(player_config, session.clone(), None, move || (backend)(None));
let player = Player::new(player_config, session.clone(), None, move || {
(backend)(None)
});
println!("Playing...");
core.run(player.load(track, true, 0)).unwrap();

View file

@ -1,6 +1,6 @@
use super::{Open, Sink};
use std::io;
use alsa::{PCM, Stream, Mode, Format, Access};
use alsa::{Access, Format, Mode, Stream, PCM};
pub struct AlsaSink(Option<PCM>, String);
@ -17,14 +17,22 @@ impl Open for AlsaSink {
impl Sink for AlsaSink {
fn start(&mut self) -> io::Result<()> {
if self.0.is_none() {
match PCM::open(&*self.1,
Stream::Playback, Mode::Blocking,
Format::Signed16, Access::Interleaved,
2, 44100) {
match PCM::open(
&*self.1,
Stream::Playback,
Mode::Blocking,
Format::Signed16,
Access::Interleaved,
2,
44100,
) {
Ok(f) => self.0 = Some(f),
Err(e) => {
error!("Alsa error PCM open {}", e);
return Err(io::Error::new(io::ErrorKind::Other, "Alsa error: PCM open failed"));
return Err(io::Error::new(
io::ErrorKind::Other,
"Alsa error: PCM open failed",
));
}
}
}

View file

@ -1,11 +1,12 @@
use std::io;
use super::{Open, Sink};
use jack::prelude::{AudioOutPort, AudioOutSpec, Client, JackControl, ProcessScope, AsyncClient, client_options, ProcessHandler, Port };
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
use jack::prelude::{client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port,
ProcessHandler, ProcessScope};
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
pub struct JackSink {
send: SyncSender<i16>,
active_client: AsyncClient<(),JackData>,
active_client: AsyncClient<(), JackData>,
}
pub struct JackData {
@ -43,14 +44,25 @@ impl Open for JackSink {
let client_name = client_name.unwrap_or("librespot".to_string());
let (client, _status) = Client::new(&client_name[..], client_options::NO_START_SERVER).unwrap();
let ch_r = client.register_port("out_0", AudioOutSpec::default()).unwrap();
let ch_l = client.register_port("out_1", AudioOutSpec::default()).unwrap();
let ch_r = client
.register_port("out_0", AudioOutSpec::default())
.unwrap();
let ch_l = client
.register_port("out_1", AudioOutSpec::default())
.unwrap();
// buffer for samples from librespot (~10ms)
let (tx, rx) = sync_channel(2*1024*4);
let jack_data = JackData { rec: rx, port_l: ch_l, port_r: ch_r };
let (tx, rx) = sync_channel(2 * 1024 * 4);
let jack_data = JackData {
rec: rx,
port_l: ch_l,
port_r: ch_r,
};
let active_client = AsyncClient::new(client, (), jack_data).unwrap();
JackSink { send: tx, active_client: active_client }
JackSink {
send: tx,
active_client: active_client,
}
}
}

View file

@ -37,9 +37,7 @@ use self::jackaudio::JackSink;
mod pipe;
use self::pipe::StdoutSink;
pub const BACKENDS : &'static [
(&'static str, fn(Option<String>) -> Box<Sink>)
] = &[
pub const BACKENDS: &'static [(&'static str, fn(Option<String>) -> Box<Sink>)] = &[
#[cfg(feature = "alsa-backend")]
("alsa", mk_sink::<AlsaSink>),
#[cfg(feature = "portaudio-backend")]
@ -53,8 +51,16 @@ pub const BACKENDS : &'static [
pub fn find(name: Option<String>) -> Option<fn(Option<String>) -> Box<Sink>> {
if let Some(name) = name {
BACKENDS.iter().find(|backend| name == backend.0).map(|backend| backend.1)
BACKENDS
.iter()
.find(|backend| name == backend.0)
.map(|backend| backend.1)
} else {
Some(BACKENDS.first().expect("No backends were enabled at build time").1)
Some(
BACKENDS
.first()
.expect("No backends were enabled at build time")
.1,
)
}
}

View file

@ -28,7 +28,10 @@ impl Sink for StdoutSink {
fn write(&mut self, data: &[i16]) -> io::Result<()> {
let data: &[u8] = unsafe {
slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::<i16>())
slice::from_raw_parts(
data.as_ptr() as *const u8,
data.len() * mem::size_of::<i16>(),
)
};
self.0.write_all(data)?;
@ -37,4 +40,3 @@ impl Sink for StdoutSink {
Ok(())
}
}

View file

@ -4,18 +4,18 @@ use std::process::exit;
use std::time::Duration;
use portaudio_rs;
use portaudio_rs::stream::*;
use portaudio_rs::device::{DeviceIndex, DeviceInfo, get_default_output_index};
use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo};
pub struct PortAudioSink<'a>(Option<portaudio_rs::stream::Stream<'a, i16, i16>>, StreamParameters<i16>);
pub struct PortAudioSink<'a>(
Option<portaudio_rs::stream::Stream<'a, i16, i16>>,
StreamParameters<i16>,
);
fn output_devices() -> Box<Iterator<Item=(DeviceIndex, DeviceInfo)>> {
fn output_devices() -> Box<Iterator<Item = (DeviceIndex, DeviceInfo)>> {
let count = portaudio_rs::device::get_count().unwrap();
let devices = (0..count)
.filter_map(|idx| {
portaudio_rs::device::get_info(idx).map(|info| (idx, info))
}).filter(|&(_, ref info)| {
info.max_output_channels > 0
});
.filter_map(|idx| portaudio_rs::device::get_info(idx).map(|info| (idx, info)))
.filter(|&(_, ref info)| info.max_output_channels > 0);
Box::new(devices)
}
@ -38,9 +38,8 @@ fn find_output(device: &str) -> Option<DeviceIndex> {
.map(|(idx, _)| idx)
}
impl <'a> Open for PortAudioSink<'a> {
impl<'a> Open for PortAudioSink<'a> {
fn open(device: Option<String>) -> PortAudioSink<'a> {
debug!("Using PortAudio sink");
portaudio_rs::initialize().unwrap();
@ -71,16 +70,19 @@ impl <'a> Open for PortAudioSink<'a> {
}
}
impl <'a> Sink for PortAudioSink<'a> {
impl<'a> Sink for PortAudioSink<'a> {
fn start(&mut self) -> io::Result<()> {
if self.0.is_none() {
self.0 = Some(Stream::open(
None, Some(self.1),
self.0 = Some(
Stream::open(
None,
Some(self.1),
44100.0,
FRAMES_PER_BUFFER_UNSPECIFIED,
StreamFlags::empty(),
None
).unwrap());;
None,
).unwrap(),
);;
}
self.0.as_mut().unwrap().start().unwrap();
@ -94,8 +96,7 @@ impl <'a> Sink for PortAudioSink<'a> {
fn write(&mut self, data: &[i16]) -> io::Result<()> {
match self.0.as_mut().unwrap().write(data) {
Ok(_) => (),
Err(portaudio_rs::PaError::OutputUnderflowed) =>
error!("PortAudio write underflow"),
Err(portaudio_rs::PaError::OutputUnderflowed) => error!("PortAudio write underflow"),
Err(e) => panic!("PA Error {}", e),
};
@ -103,7 +104,7 @@ impl <'a> Sink for PortAudioSink<'a> {
}
}
impl <'a> Drop for PortAudioSink<'a> {
impl<'a> Drop for PortAudioSink<'a> {
fn drop(&mut self) {
portaudio_rs::terminate().unwrap();
}

View file

@ -8,13 +8,14 @@ use std::mem;
use libc;
pub struct PulseAudioSink {
s : *mut pa_simple,
ss : pa_sample_spec,
name : CString,
desc : CString
s: *mut pa_simple,
ss: pa_sample_spec,
name: CString,
desc: CString,
}
fn call_pulseaudio<T, F, FailCheck>(f: F, fail_check: FailCheck, kind: io::ErrorKind) -> io::Result<T> where
fn call_pulseaudio<T, F, FailCheck>(f: F, fail_check: FailCheck, kind: io::ErrorKind) -> io::Result<T>
where
T: Copy,
F: Fn(*mut libc::c_int) -> T,
FailCheck: Fn(T) -> bool,
@ -58,7 +59,7 @@ impl Open for PulseAudioSink {
let ss = pa_sample_spec {
format: PA_SAMPLE_S16LE,
channels: 2, // stereo
rate: 44100
rate: 44100,
};
let name = CString::new("librespot").unwrap();
@ -68,7 +69,7 @@ impl Open for PulseAudioSink {
s: null_mut(),
ss: ss,
name: name,
desc: description
desc: description,
}
}
}
@ -78,7 +79,8 @@ impl Sink for PulseAudioSink {
if self.s == null_mut() {
self.s = call_pulseaudio(
|err| unsafe {
pa_simple_new(null(), // Use the default server.
pa_simple_new(
null(), // Use the default server.
self.name.as_ptr(), // Our application's name.
PA_STREAM_PLAYBACK,
null(), // Use the default device.
@ -86,10 +88,12 @@ impl Sink for PulseAudioSink {
&self.ss, // Our sample format.
null(), // Use default channel map
null(), // Use default buffering attributes.
err)
err,
)
},
|ptr| ptr == null_mut(),
io::ErrorKind::ConnectionRefused)?;
io::ErrorKind::ConnectionRefused,
)?;
}
Ok(())
}
@ -101,17 +105,18 @@ impl Sink for PulseAudioSink {
fn write(&mut self, data: &[i16]) -> io::Result<()> {
if self.s == null_mut() {
Err(io::Error::new(io::ErrorKind::NotConnected, "Not connected to pulseaudio"))
}
else {
Err(io::Error::new(
io::ErrorKind::NotConnected,
"Not connected to pulseaudio",
))
} else {
let ptr = data.as_ptr() as *const libc::c_void;
let len = data.len() as usize * mem::size_of::<i16>();
call_pulseaudio(
|err| unsafe {
pa_simple_write(self.s, ptr, len, err)
},
|err| unsafe { pa_simple_write(self.s, ptr, len, err) },
|ret| ret < 0,
io::ErrorKind::BrokenPipe)?;
io::ErrorKind::BrokenPipe,
)?;
Ok(())
}
}

View file

@ -1,7 +1,8 @@
#[macro_use] extern crate log;
#[macro_use]
extern crate log;
extern crate futures;
extern crate byteorder;
extern crate futures;
#[cfg(feature = "alsa-backend")]
extern crate alsa;

View file

@ -1,5 +1,7 @@
pub trait Mixer : Send {
fn open() -> Self where Self: Sized;
pub trait Mixer: Send {
fn open() -> Self
where
Self: Sized;
fn start(&self);
fn stop(&self);
fn set_volume(&self, volume: u16);

View file

@ -6,19 +6,17 @@ use super::AudioFilter;
#[derive(Clone)]
pub struct SoftMixer {
volume: Arc<AtomicUsize>
volume: Arc<AtomicUsize>,
}
impl Mixer for SoftMixer {
fn open() -> SoftMixer {
SoftMixer {
volume: Arc::new(AtomicUsize::new(0xFFFF))
volume: Arc::new(AtomicUsize::new(0xFFFF)),
}
}
fn start(&self) {
}
fn stop(&self) {
}
fn start(&self) {}
fn stop(&self) {}
fn volume(&self) -> u16 {
self.volume.load(Ordering::Relaxed) as u16
}
@ -26,12 +24,14 @@ impl Mixer for SoftMixer {
self.volume.store(volume as usize, Ordering::Relaxed);
}
fn get_audio_filter(&self) -> Option<Box<AudioFilter + Send>> {
Some(Box::new(SoftVolumeApplier { volume: self.volume.clone() }))
Some(Box::new(SoftVolumeApplier {
volume: self.volume.clone(),
}))
}
}
struct SoftVolumeApplier {
volume: Arc<AtomicUsize>
volume: Arc<AtomicUsize>,
}
impl AudioFilter for SoftVolumeApplier {

View file

@ -3,10 +3,10 @@ use futures::sync::oneshot;
use futures::{future, Future};
use std;
use std::borrow::Cow;
use std::io::{Read, Seek, SeekFrom, Result};
use std::io::{Read, Result, Seek, SeekFrom};
use std::mem;
use std::process::Command;
use std::sync::mpsc::{RecvError, TryRecvError, RecvTimeoutError};
use std::sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError};
use std::thread;
use std::time::Duration;
@ -15,9 +15,9 @@ use core::session::Session;
use core::spotify_id::SpotifyId;
use audio_backend::Sink;
use audio::{AudioFile, AudioDecrypt};
use audio::{AudioDecrypt, AudioFile};
use audio::{VorbisDecoder, VorbisPacket};
use metadata::{FileFormat, Track, Metadata};
use metadata::{FileFormat, Metadata, Track};
use mixer::AudioFilter;
pub struct Player {
@ -53,10 +53,14 @@ struct NormalisationConfig {
}
impl Player {
pub fn new<F>(config: PlayerConfig, session: Session,
pub fn new<F>(
config: PlayerConfig,
session: Session,
audio_filter: Option<Box<AudioFilter + Send>>,
sink_builder: F) -> Player
where F: FnOnce() -> Box<Sink> + Send + 'static
sink_builder: F,
) -> Player
where
F: FnOnce() -> Box<Sink> + Send + 'static,
{
let (cmd_tx, cmd_rx) = std::sync::mpsc::channel();
@ -87,9 +91,12 @@ impl Player {
self.commands.as_ref().unwrap().send(cmd).unwrap();
}
pub fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32)
-> oneshot::Receiver<()>
{
pub fn load(
&self,
track: SpotifyId,
start_playing: bool,
position_ms: u32,
) -> oneshot::Receiver<()> {
let (tx, rx) = oneshot::channel();
self.command(PlayerCommand::Load(track, start_playing, position_ms, tx));
@ -120,7 +127,7 @@ impl Drop for Player {
if let Some(handle) = self.thread_handle.take() {
match handle.join() {
Ok(_) => (),
Err(_) => error!("Player thread panicked!")
Err(_) => error!("Player thread panicked!"),
}
}
}
@ -157,8 +164,12 @@ impl PlayerState {
use self::PlayerState::*;
match *self {
Stopped => None,
Paused { ref mut decoder, .. } |
Playing { ref mut decoder, .. } => Some(decoder),
Paused {
ref mut decoder, ..
}
| Playing {
ref mut decoder, ..
} => Some(decoder),
Invalid => panic!("invalid state"),
}
}
@ -166,8 +177,7 @@ impl PlayerState {
fn signal_end_of_track(self) {
use self::PlayerState::*;
match self {
Paused { end_of_track, .. } |
Playing { end_of_track, .. } => {
Paused { end_of_track, .. } | Playing { end_of_track, .. } => {
let _ = end_of_track.send(());
}
@ -179,7 +189,11 @@ impl PlayerState {
fn paused_to_playing(&mut self) {
use self::PlayerState::*;
match ::std::mem::replace(self, Invalid) {
Paused { decoder, end_of_track, normalisation_factor } => {
Paused {
decoder,
end_of_track,
normalisation_factor,
} => {
*self = Playing {
decoder: decoder,
end_of_track: end_of_track,
@ -193,7 +207,11 @@ impl PlayerState {
fn playing_to_paused(&mut self) {
use self::PlayerState::*;
match ::std::mem::replace(self, Invalid) {
Playing { decoder, end_of_track, normalisation_factor } => {
Playing {
decoder,
end_of_track,
normalisation_factor,
} => {
*self = Paused {
decoder: decoder,
end_of_track: end_of_track,
@ -209,16 +227,13 @@ impl PlayerInternal {
fn run(mut self) {
loop {
let cmd = if self.state.is_playing() {
if self.sink_running
{
if self.sink_running {
match self.commands.try_recv() {
Ok(cmd) => Some(cmd),
Err(TryRecvError::Empty) => None,
Err(TryRecvError::Disconnected) => return,
}
}
else
{
} else {
match self.commands.recv_timeout(Duration::from_secs(5)) {
Ok(cmd) => Some(cmd),
Err(RecvTimeoutError::Timeout) => None,
@ -236,14 +251,19 @@ impl PlayerInternal {
self.handle_command(cmd);
}
if self.state.is_playing() && ! self.sink_running {
if self.state.is_playing() && !self.sink_running {
self.start_sink();
}
if self.sink_running {
let mut current_normalisation_factor: f32 = 1.0;
let packet = if let PlayerState::Playing { ref mut decoder, normalisation_factor, .. } = self.state {
let packet = if let PlayerState::Playing {
ref mut decoder,
normalisation_factor,
..
} = self.state
{
current_normalisation_factor = normalisation_factor;
Some(decoder.next_packet().expect("Vorbis error"))
} else {
@ -380,8 +400,7 @@ impl PlayerInternal {
}
}
PlayerCommand::Stop => {
match self.state {
PlayerCommand::Stop => match self.state {
PlayerState::Playing { .. } => {
self.stop_sink_if_running();
self.run_onstop();
@ -389,13 +408,12 @@ impl PlayerInternal {
}
PlayerState::Paused { .. } => {
self.state = PlayerState::Stopped;
},
}
PlayerState::Stopped => {
warn!("Player::stop called from invalid state");
}
PlayerState::Invalid => panic!("invalid state"),
}
}
},
}
}
@ -415,14 +433,16 @@ impl PlayerInternal {
if track.available {
Some(Cow::Borrowed(track))
} else {
let alternatives = track.alternatives
let alternatives = track
.alternatives
.iter()
.map(|alt_id| {
Track::get(&self.session, *alt_id)
});
.map(|alt_id| Track::get(&self.session, *alt_id));
let alternatives = future::join_all(alternatives).wait().unwrap();
alternatives.into_iter().find(|alt| alt.available).map(Cow::Owned)
alternatives
.into_iter()
.find(|alt| alt.available)
.map(Cow::Owned)
}
}
@ -478,12 +498,19 @@ impl PlayerInternal {
let file_id = match track.files.get(&format) {
Some(&file_id) => file_id,
None => {
warn!("Track \"{}\" is not available in format {:?}", track.name, format);
warn!(
"Track \"{}\" is not available in format {:?}",
track.name, format
);
return None;
}
};
let key = self.session.audio_key().request(track.id, file_id).wait().unwrap();
let key = self.session
.audio_key()
.request(track.id, file_id)
.wait()
.unwrap();
let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap();
@ -493,7 +520,10 @@ impl PlayerInternal {
if self.config.normalisation {
let normalisation_config = self.parse_normalisation(&mut decrypted_file);
normalisation_factor = f32::powf(10.0, (normalisation_config.track_gain_db + self.config.normalisation_pregain) / 20.0);
normalisation_factor = f32::powf(
10.0,
(normalisation_config.track_gain_db + self.config.normalisation_pregain) / 20.0,
);
if normalisation_factor * normalisation_config.track_peak > 1.0 {
debug!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid.");
@ -527,27 +557,15 @@ impl Drop for PlayerInternal {
impl ::std::fmt::Debug for PlayerCommand {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
PlayerCommand::Load(track, play, position, _) => {
f.debug_tuple("Load")
PlayerCommand::Load(track, play, position, _) => f.debug_tuple("Load")
.field(&track)
.field(&play)
.field(&position)
.finish()
}
PlayerCommand::Play => {
f.debug_tuple("Play").finish()
}
PlayerCommand::Pause => {
f.debug_tuple("Pause").finish()
}
PlayerCommand::Stop => {
f.debug_tuple("Stop").finish()
}
PlayerCommand::Seek(position) => {
f.debug_tuple("Seek")
.field(&position)
.finish()
}
.finish(),
PlayerCommand::Play => f.debug_tuple("Play").finish(),
PlayerCommand::Pause => f.debug_tuple("Pause").finish(),
PlayerCommand::Stop => f.debug_tuple("Stop").finish(),
PlayerCommand::Seek(position) => f.debug_tuple("Seek").field(&position).finish(),
}
}
}

View file

@ -7,7 +7,10 @@ fn main() {
for &(path, expected_checksum) in files::FILES {
let actual = cksum_file(path).unwrap();
if expected_checksum != actual {
panic!("Checksum for {:?} does not match. Try running build.sh", path);
panic!(
"Checksum for {:?} does not match. Try running build.sh",
path
);
}
}
}
@ -37,69 +40,37 @@ fn cksum<T: AsRef<[u8]>>(data: T) -> u32 {
!value
}
static CRC_LOOKUP_ARRAY : &'static[u32] = &[
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
static CRC_LOOKUP_ARRAY: &'static [u32] = &[
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
];

View file

@ -1,6 +1,6 @@
// Autogenerated by build.sh
pub const FILES : &'static [(&'static str, u32)] = &[
pub const FILES: &'static [(&'static str, u32)] = &[
("proto/authentication.proto", 2098196376),
("proto/keyexchange.proto", 451735664),
("proto/mercury.proto", 709993906),

View file

@ -1,5 +1,4 @@
#![crate_name = "librespot"]
#![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))]
extern crate base64;
@ -15,6 +14,6 @@ extern crate url;
pub extern crate librespot_audio as audio;
pub extern crate librespot_connect as connect;
pub extern crate librespot_core as core;
pub extern crate librespot_metadata as metadata;
pub extern crate librespot_playback as playback;
pub extern crate librespot_protocol as protocol;
pub extern crate librespot_metadata as metadata;

View file

@ -1,26 +1,27 @@
#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
#[macro_use]
extern crate log;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_signal;
use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use futures::{Async, Future, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::reactor::{Core, Handle};
use tokio_io::IoStream;
use std::mem;
use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{DeviceType, SessionConfig, ConnectConfig};
use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig};
use librespot::core::session::Session;
use librespot::core::version;
@ -87,29 +88,86 @@ struct Setup {
fn setup(args: &[String]) -> Setup {
let mut opts = getopts::Options::new();
opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
.optflag("", "disable-audio-cache", "Disable caching of the audio data.")
opts.optopt(
"c",
"cache",
"Path to a directory where files will be cached.",
"CACHE",
).optflag(
"",
"disable-audio-cache",
"Disable caching of the audio data.",
)
.reqopt("n", "name", "Device name", "NAME")
.optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
.optopt(
"b",
"bitrate",
"Bitrate (96, 160 or 320). Defaults to 160",
"BITRATE",
)
.optopt(
"",
"onstart",
"Run PROGRAM when playback is about to begin.",
"PROGRAM",
)
.optopt(
"",
"onstop",
"Run PROGRAM when playback has ended.",
"PROGRAM",
)
.optflag("v", "verbose", "Enable verbose output")
.optopt("u", "username", "Username to sign in with", "USERNAME")
.optopt("p", "password", "Password", "PASSWORD")
.optflag("", "disable-discovery", "Disable discovery mode")
.optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
.optopt("", "device", "Audio device to use. Use '?' to list options if using portaudio", "DEVICE")
.optopt(
"",
"backend",
"Audio backend to use. Use '?' to list options",
"BACKEND",
)
.optopt(
"",
"device",
"Audio device to use. Use '?' to list options if using portaudio",
"DEVICE",
)
.optopt("", "mixer", "Mixer to use", "MIXER")
.optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME")
.optopt("", "zeroconf-port", "The port the internal server advertised over zeroconf uses.", "ZEROCONF_PORT")
.optflag("", "enable-volume-normalisation", "Play all tracks at the same volume")
.optopt("", "normalisation-pregain", "Pregain (dB) applied by volume normalisation", "PREGAIN");
.optopt(
"",
"initial-volume",
"Initial volume in %, once connected (must be from 0 to 100)",
"VOLUME",
)
.optopt(
"",
"zeroconf-port",
"The port the internal server advertised over zeroconf uses.",
"ZEROCONF_PORT",
)
.optflag(
"",
"enable-volume-normalisation",
"Play all tracks at the same volume",
)
.optopt(
"",
"normalisation-pregain",
"Pregain (dB) applied by volume normalisation",
"PREGAIN",
);
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(f) => {
writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
writeln!(
stderr(),
"error: {}\n{}",
f.to_string(),
usage(&args[0], &opts)
).unwrap();
exit(1);
}
};
@ -117,11 +175,13 @@ fn setup(args: &[String]) -> Setup {
let verbose = matches.opt_present("verbose");
setup_logging(verbose);
info!("librespot {} ({}). Built on {}. Build ID: {}",
info!(
"librespot {} ({}). Built on {}. Build ID: {}",
version::short_sha(),
version::commit_date(),
version::short_now(),
version::build_id());
version::build_id()
);
let backend_name = matches.opt_str("backend");
if backend_name == Some("?".into()) {
@ -129,14 +189,12 @@ fn setup(args: &[String]) -> Setup {
exit(0);
}
let backend = audio_backend::find(backend_name)
.expect("Invalid backend");
let backend = audio_backend::find(backend_name).expect("Invalid backend");
let device = matches.opt_str("device");
let mixer_name = matches.opt_str("mixer");
let mixer = mixer::find(mixer_name.as_ref())
.expect("Invalid mixer");
let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");
let initial_volume = matches
.opt_str("initial-volume")
@ -149,17 +207,17 @@ fn setup(args: &[String]) -> Setup {
})
.unwrap_or(0x8000);
let zeroconf_port =
matches.opt_str("zeroconf-port")
let zeroconf_port = matches
.opt_str("zeroconf-port")
.map(|port| port.parse::<u16>().unwrap())
.unwrap_or(0);
let name = matches.opt_str("name").unwrap();
let use_audio_cache = !matches.opt_present("disable-audio-cache");
let cache = matches.opt_str("c").map(|cache_location| {
Cache::new(PathBuf::from(cache_location), use_audio_cache)
});
let cache = matches
.opt_str("c")
.map(|cache_location| Cache::new(PathBuf::from(cache_location), use_audio_cache));
let credentials = {
let cached_credentials = cache.as_ref().and_then(Cache::credentials);
@ -167,7 +225,7 @@ fn setup(args: &[String]) -> Setup {
get_credentials(
matches.opt_str("username"),
matches.opt_str("password"),
cached_credentials
cached_credentials,
)
};
@ -181,7 +239,9 @@ fn setup(args: &[String]) -> Setup {
};
let player_config = {
let bitrate = matches.opt_str("b").as_ref()
let bitrate = matches
.opt_str("b")
.as_ref()
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
.unwrap_or(Bitrate::default());
@ -190,14 +250,17 @@ fn setup(args: &[String]) -> Setup {
onstart: matches.opt_str("onstart"),
onstop: matches.opt_str("onstop"),
normalisation: matches.opt_present("enable-volume-normalisation"),
normalisation_pregain: matches.opt_str("normalisation-pregain")
normalisation_pregain: matches
.opt_str("normalisation-pregain")
.map(|pregain| pregain.parse::<f32>().expect("Invalid pregain float value"))
.unwrap_or(PlayerConfig::default().normalisation_pregain),
}
};
let connect_config = {
let device_type = matches.opt_str("device-type").as_ref()
let device_type = matches
.opt_str("device-type")
.as_ref()
.map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
.unwrap_or(DeviceType::default());
@ -239,7 +302,7 @@ struct Main {
spirc: Option<Spirc>,
spirc_task: Option<SpircTask>,
connect: Box<Future<Item=Session, Error=io::Error>>,
connect: Box<Future<Item = Session, Error = io::Error>>,
shutdown: bool,
}