Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metadata pipe support which is compatible with forked-daapd. #214

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions playback/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ path = "../core"
path = "../metadata"

[dependencies]
base64 = "0.5.0"
futures = "0.1.8"
log = "0.3.5"
byteorder = "1.2.1"
Expand Down
2 changes: 1 addition & 1 deletion playback/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[macro_use]
extern crate log;

extern crate base64;
extern crate byteorder;
extern crate futures;

Expand Down
5 changes: 5 additions & 0 deletions playback/src/mixer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub trait Mixer: Send {
fn get_audio_filter(&self) -> Option<Box<AudioFilter + Send>> {
None
}
fn set_metadata_pipe(&mut self, _metadata_pipe: Option<String>) {}
}

pub trait AudioFilter {
Expand All @@ -18,13 +19,17 @@ pub trait AudioFilter {
pub mod softmixer;
use self::softmixer::SoftMixer;

pub mod pipemixer;
use self::pipemixer::PipeMixer;

fn mk_sink<M: Mixer + 'static>() -> Box<Mixer> {
Box::new(M::open())
}

pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<fn() -> Box<Mixer>> {
match name.as_ref().map(AsRef::as_ref) {
None | Some("softvol") => Some(mk_sink::<SoftMixer>),
Some("pipe") => Some(mk_sink::<PipeMixer>),
_ => None,
}
}
56 changes: 56 additions & 0 deletions playback/src/mixer/pipemixer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use base64;
use std::f32;
use std::fs::File;
use std::io::Write;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

use super::Mixer;

#[derive(Clone)]
pub struct PipeMixer {
volume: Arc<AtomicUsize>,
pipe: Option<String>,
}

impl Mixer for PipeMixer {
fn open() -> PipeMixer {
PipeMixer {
volume: Arc::new(AtomicUsize::new(0xFFFF)),
pipe: None,
}
}
fn start(&self) {}
fn stop(&self) {}
fn volume(&self) -> u16 {
self.volume.load(Ordering::Relaxed) as u16
}
fn set_volume(&self, volume: u16) {
self.volume.store(volume as usize, Ordering::Relaxed);

if let Some(path) = self.pipe.as_ref() {
let vol = volume;
let metadata_vol = if vol == 0 {
-144.0f32
} else if vol == 1 {
-30.0f32
} else if vol == 0xFFFF {
0.0f32
} else {
((vol as f32) - (0xFFFF as f32)) * 30.0f32 / (0xFFFE as f32)
};

let vol_string = format!("{:.*},0.00,0.00,0.00", 2, metadata_vol);
let vol_string_len = vol_string.chars().count();
let metadata_vol_string = base64::encode(&vol_string);
let metadata_xml = format!("<item><type>73736e63</type><code>70766f6c</code><length>{}</length>\n<data encoding=\"base64\">\n{}</data></item>", vol_string_len, metadata_vol_string);

let mut f = File::create(path).expect("Unable to open pipe");
f.write_all(metadata_xml.as_bytes())
.expect("Unable to write data");
}
}
fn set_metadata_pipe(&mut self, metadata_pipe: Option<String>) {
self.pipe = metadata_pipe;
}
}
42 changes: 41 additions & 1 deletion playback/src/player.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use byteorder::{LittleEndian, ReadBytesExt};
use futures;
use base64;
use futures::sync::oneshot;
use futures::{future, Future};
use std;
Expand All @@ -9,6 +10,8 @@ use std::mem;
use std::sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError};
use std::thread;
use std::time::Duration;
use std::fs::File;
use std::io::Write;

use config::{Bitrate, PlayerConfig};
use core::session::Session;
Expand All @@ -17,7 +20,7 @@ use core::spotify_id::SpotifyId;
use audio::{AudioDecrypt, AudioFile};
use audio::{VorbisDecoder, VorbisPacket};
use audio_backend::Sink;
use metadata::{FileFormat, Metadata, Track};
use metadata::{FileFormat, Metadata, Track, Album, Artist};
use mixer::AudioFilter;

pub struct Player {
Expand All @@ -35,6 +38,8 @@ struct PlayerInternal {
sink_running: bool,
audio_filter: Option<Box<AudioFilter + Send>>,
event_sender: futures::sync::mpsc::UnboundedSender<PlayerEvent>,

metadata_pipe: Option<String>,
}

enum PlayerCommand {
Expand Down Expand Up @@ -113,6 +118,7 @@ impl Player {
config: PlayerConfig,
session: Session,
audio_filter: Option<Box<AudioFilter + Send>>,
metadata_pipe: Option<String>,
sink_builder: F,
) -> (Player, PlayerEventChannel)
where
Expand All @@ -133,6 +139,7 @@ impl Player {
sink: sink_builder(),
sink_running: false,
audio_filter: audio_filter,
metadata_pipe: metadata_pipe,
event_sender: event_sender,
};

Expand Down Expand Up @@ -535,6 +542,39 @@ impl PlayerInternal {
track_id.to_base62()
);

if let Some(path) = self.metadata_pipe.as_ref() {
let mut f = File::create(path).expect("Unable to open pipe");

// title
let title = track.name.clone();
let title_len = title.chars().count();
let title_string = base64::encode(&title);
let title_xml = format!("<item><type>636f7265</type><code>6d696e6d</code><length>{}</length>\n<data encoding=\"base64\">\n{}</data></item>", title_len, title_string);
f.write_all(title_xml.as_bytes()).expect("Unable to write title");

// album
let album = Album::get(&self.session, track.album).wait().unwrap();
let album_name = album.name.clone();
let album_name_len = album_name.chars().count();
let album_name_string = base64::encode(&album_name);
let album_name_xml = format!("<item><type>636f7265</type><code>6173616c</code><length>{}</length>\n<data encoding=\"base64\">\n{}</data></item>", album_name_len, album_name_string);
f.write_all(album_name_xml.as_bytes()).expect("Unable to write album");

// artist
let mut artists = String::new();
for id in &track.artists {
if artists != "" {
artists.push_str(" & ");
}
let artist = Artist::get(&self.session, *id).wait().unwrap();
artists.push_str(&artist.name);
}
let artists_len = artists.chars().count();
let artists_string = base64::encode(&artists);
let artists_xml = format!("<item><type>636f7265</type><code>61736172</code><length>{}</length>\n<data encoding=\"base64\">\n{}</data></item>", artists_len, artists_string);
f.write_all(artists_xml.as_bytes()).expect("Unable to write artists");
}

let track = match self.find_available_alternative(&track) {
Some(track) => track,
None => {
Expand Down
23 changes: 18 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ fn list_backends() {
struct Setup {
backend: fn(Option<String>) -> Box<Sink>,
device: Option<String>,
metadata_pipe: Option<String>,

mixer: fn() -> Box<Mixer>,

Expand Down Expand Up @@ -143,6 +144,7 @@ fn setup(args: &[String]) -> Setup {
"DEVICE",
)
.optopt("", "mixer", "Mixer to use", "MIXER")
.optopt("", "metadata-pipe", "Pipe to write metadata", "METADATA_PIPE")
.optopt(
"",
"initial-volume",
Expand Down Expand Up @@ -201,6 +203,8 @@ fn setup(args: &[String]) -> Setup {

let device = matches.opt_str("device");

let metadata_pipe = matches.opt_str("metadata-pipe");

let mixer_name = matches.opt_str("mixer");
let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");

Expand Down Expand Up @@ -312,6 +316,7 @@ fn setup(args: &[String]) -> Setup {
connect_config: connect_config,
credentials: credentials,
device: device,
metadata_pipe: metadata_pipe,
enable_discovery: enable_discovery,
zeroconf_port: zeroconf_port,
mixer: mixer,
Expand All @@ -326,6 +331,7 @@ struct Main {
connect_config: ConnectConfig,
backend: fn(Option<String>) -> Box<Sink>,
device: Option<String>,
metadata_pipe: Option<String>,
mixer: fn() -> Box<Mixer>,
handle: Handle,

Expand All @@ -352,6 +358,7 @@ impl Main {
connect_config: setup.connect_config,
backend: setup.backend,
device: setup.device,
metadata_pipe: setup.metadata_pipe,
mixer: setup.mixer,

connect: Box::new(futures::future::empty()),
Expand Down Expand Up @@ -414,16 +421,22 @@ impl Future for Main {
if let Async::Ready(session) = self.connect.poll().unwrap() {
self.connect = Box::new(futures::future::empty());
let device = self.device.clone();
let mixer = (self.mixer)();
let mut mixer = (self.mixer)();
let player_config = self.player_config.clone();
let connect_config = self.connect_config.clone();
let metadata_pipe = self.metadata_pipe.clone();

mixer.set_metadata_pipe(metadata_pipe.clone());

let audio_filter = mixer.get_audio_filter();
let backend = self.backend;
let (player, event_channel) =
Player::new(player_config, session.clone(), audio_filter, move || {
(backend)(device)
});
let (player, event_channel) = Player::new(
player_config,
session.clone(),
audio_filter,
metadata_pipe,
move || (backend)(device),
);

let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
self.spirc = Some(spirc);
Expand Down