Merge branch 'master' into events-on-prev-next

This commit is contained in:
Sasha Hilton 2018-02-24 20:16:28 +01:00 committed by GitHub
commit bde157fad7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 231 additions and 208 deletions

85
Cargo.lock generated
View file

@ -14,14 +14,6 @@ dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "aster"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.5.2" version = "0.5.2"
@ -62,11 +54,6 @@ name = "bitflags"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.0.1" version = "1.0.1"
@ -324,7 +311,6 @@ dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -371,7 +357,6 @@ dependencies = [
"mdns 0.2.0 (git+https://github.com/plietar/rust-mdns)", "mdns 0.2.0 (git+https://github.com/plietar/rust-mdns)",
"num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
@ -428,6 +413,7 @@ name = "librespot-playback"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"alsa 0.0.1 (git+https://github.com/plietar/rust-alsa)", "alsa 0.0.1 (git+https://github.com/plietar/rust-alsa)",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"jack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "jack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -655,16 +641,6 @@ name = "protobuf"
version = "1.4.3" version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "protobuf_macros"
version = "0.6.0"
source = "git+https://github.com/plietar/rust-protobuf-macros#f186dc5a16c0d79f14c319ac8ce30b06de0cefee"
dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "quick-error" name = "quick-error"
version = "1.2.1" version = "1.2.1"
@ -833,48 +809,6 @@ dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "syntex"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_errors"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_pos"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_syntax"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "take" name = "take"
version = "0.1.0" version = "0.1.0"
@ -892,15 +826,6 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "term"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "termios" name = "termios"
version = "0.2.2" version = "0.2.2"
@ -1177,14 +1102,12 @@ dependencies = [
[metadata] [metadata]
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum alsa 0.0.1 (git+https://github.com/plietar/rust-alsa)" = "<none>" "checksum alsa 0.0.1 (git+https://github.com/plietar/rust-alsa)" = "<none>"
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557" "checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4" "checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4"
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
"checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c" "checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
@ -1241,7 +1164,6 @@ dependencies = [
"checksum portaudio-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "029e0ab393b44b2d825efbc755cae51c36be7a99d91356b2052be0ed05836636" "checksum portaudio-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "029e0ab393b44b2d825efbc755cae51c36be7a99d91356b2052be0ed05836636"
"checksum portaudio-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5194a4fa953b4ffd851c320ef6f0484cd7278cb7169ea9d6c433e49b23f7b7f5" "checksum portaudio-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5194a4fa953b4ffd851c320ef6f0484cd7278cb7169ea9d6c433e49b23f7b7f5"
"checksum protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bec26e67194b7d991908145fdf21b7cae8b08423d96dcb9e860cd31f854b9506" "checksum protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bec26e67194b7d991908145fdf21b7cae8b08423d96dcb9e860cd31f854b9506"
"checksum protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)" = "<none>"
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1" "checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
@ -1265,13 +1187,8 @@ dependencies = [
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" "checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0"
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
"checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" "checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"

View file

@ -55,7 +55,6 @@ url = "1.3"
[build-dependencies] [build-dependencies]
rand = "0.3.13" rand = "0.3.13"
vergen = "0.1.0" vergen = "0.1.0"
protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros", features = ["with-syntex"] }
[replace] [replace]
"rust-crypto:0.2.36" = { git = "https://github.com/awmath/rust-crypto.git", branch = "avx2" } "rust-crypto:0.2.36" = { git = "https://github.com/awmath/rust-crypto.git", branch = "avx2" }

View file

@ -2,7 +2,6 @@
name = "librespot-connect" name = "librespot-connect"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Lietar <paul@lietar.net>"] authors = ["Paul Lietar <paul@lietar.net>"]
build = "build.rs"
[dependencies.librespot-core] [dependencies.librespot-core]
path = "../core" path = "../core"
@ -29,9 +28,6 @@ url = "1.3"
dns-sd = { version = "0.1.3", optional = true } dns-sd = { version = "0.1.3", optional = true }
mdns = { git = "https://github.com/plietar/rust-mdns", optional = true } mdns = { git = "https://github.com/plietar/rust-mdns", optional = true }
[build-dependencies]
protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros", features = ["with-syntex"] }
[features] [features]
default = ["mdns"] default = ["mdns"]
with-dns-sd = ["dns-sd"] with-dns-sd = ["dns-sd"]

View file

@ -1,13 +0,0 @@
extern crate protobuf_macros;
use std::env;
use std::path::PathBuf;
fn main() {
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
protobuf_macros::expand("src/lib.in.rs", &out.join("lib.rs")).unwrap();
println!("cargo:rerun-if-changed=src/lib.in.rs");
println!("cargo:rerun-if-changed=src/spirc.rs");
}

View file

@ -1,2 +0,0 @@
#[allow(unused_mut)]
pub mod spirc;

View file

@ -24,5 +24,4 @@ extern crate librespot_playback as playback;
extern crate librespot_protocol as protocol; extern crate librespot_protocol as protocol;
pub mod discovery; pub mod discovery;
pub mod spirc;
include!(concat!(env!("OUT_DIR"), "/lib.rs"));

View file

@ -64,71 +64,110 @@ fn now_ms() -> i64 {
} }
fn initial_state() -> State { fn initial_state() -> State {
protobuf_init!(protocol::spirc::State::new(), { let mut frame = protocol::spirc::State::new();
repeat: false, frame.set_repeat(false);
shuffle: false, frame.set_shuffle(false);
status: PlayStatus::kPlayStatusStop, frame.set_status(PlayStatus::kPlayStatusStop);
position_ms: 0, frame.set_position_ms(0);
position_measured_at: 0, frame.set_position_measured_at(0);
}) frame
} }
fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState { fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState {
protobuf_init!(DeviceState::new(), { {
sw_version: version::version_string(), let mut msg = DeviceState::new();
is_active: false, msg.set_sw_version(version::version_string());
can_play: true, msg.set_is_active(false);
volume: volume as u32, msg.set_can_play(true);
name: config.name, msg.set_volume(volume as u32);
capabilities => [ msg.set_name(config.name);
@{ {
typ: protocol::spirc::CapabilityType::kCanBePlayer, let repeated = msg.mut_capabilities();
intValue => [1] {
}, let msg = repeated.push_default();
@{ msg.set_typ(protocol::spirc::CapabilityType::kCanBePlayer);
typ: protocol::spirc::CapabilityType::kDeviceType, {
intValue => [config.device_type as i64] let repeated = msg.mut_intValue();
}, repeated.push(1)
@{ };
typ: protocol::spirc::CapabilityType::kGaiaEqConnectId, msg
intValue => [1] };
}, {
@{ let msg = repeated.push_default();
typ: protocol::spirc::CapabilityType::kSupportsLogout, msg.set_typ(protocol::spirc::CapabilityType::kDeviceType);
intValue => [0] {
}, let repeated = msg.mut_intValue();
@{ repeated.push(config.device_type as i64)
typ: protocol::spirc::CapabilityType::kIsObservable, };
intValue => [1] msg
}, };
@{ {
typ: protocol::spirc::CapabilityType::kVolumeSteps, let msg = repeated.push_default();
intValue => [64] msg.set_typ(protocol::spirc::CapabilityType::kGaiaEqConnectId);
}, {
@{ let repeated = msg.mut_intValue();
typ: protocol::spirc::CapabilityType::kSupportedContexts, repeated.push(1)
stringValue => [ };
"album", msg
"playlist", };
"search", {
"inbox", let msg = repeated.push_default();
"toplist", msg.set_typ(protocol::spirc::CapabilityType::kSupportsLogout);
"starred", {
"publishedstarred", let repeated = msg.mut_intValue();
"track", repeated.push(0)
] };
}, msg
@{ };
typ: protocol::spirc::CapabilityType::kSupportedTypes, {
stringValue => [ let msg = repeated.push_default();
"audio/local", msg.set_typ(protocol::spirc::CapabilityType::kIsObservable);
"audio/track", {
"local", let repeated = msg.mut_intValue();
"track", repeated.push(1)
] };
} msg
], };
}) {
let msg = repeated.push_default();
msg.set_typ(protocol::spirc::CapabilityType::kVolumeSteps);
{
let repeated = msg.mut_intValue();
repeated.push(64)
};
msg
};
{
let msg = repeated.push_default();
msg.set_typ(protocol::spirc::CapabilityType::kSupportedContexts);
{
let repeated = msg.mut_stringValue();
repeated.push(::std::convert::Into::into("album"));
repeated.push(::std::convert::Into::into("playlist"));
repeated.push(::std::convert::Into::into("search"));
repeated.push(::std::convert::Into::into("inbox"));
repeated.push(::std::convert::Into::into("toplist"));
repeated.push(::std::convert::Into::into("starred"));
repeated.push(::std::convert::Into::into("publishedstarred"));
repeated.push(::std::convert::Into::into("track"))
};
msg
};
{
let msg = repeated.push_default();
msg.set_typ(protocol::spirc::CapabilityType::kSupportedTypes);
{
let repeated = msg.mut_stringValue();
repeated.push(::std::convert::Into::into("audio/local"));
repeated.push(::std::convert::Into::into("audio/track"));
repeated.push(::std::convert::Into::into("local"));
repeated.push(::std::convert::Into::into("track"))
};
msg
};
};
msg
}
} }
fn volume_to_mixer(volume: u16) -> u16 { fn volume_to_mixer(volume: u16) -> u16 {
@ -628,9 +667,13 @@ impl SpircTask {
fn update_tracks(&mut self, frame: &protocol::spirc::Frame) { fn update_tracks(&mut self, frame: &protocol::spirc::Frame) {
let index = frame.get_state().get_playing_track_index(); let index = frame.get_state().get_playing_track_index();
let tracks = frame.get_state().get_track(); let tracks = frame.get_state().get_track();
let context_uri = frame.get_state().get_context_uri().to_owned();
self.state.set_playing_track_index(index); self.state.set_playing_track_index(index);
self.state.set_track(tracks.into_iter().cloned().collect()); self.state.set_track(tracks.into_iter().cloned().collect());
self.state.set_context_uri(context_uri);
self.state.set_repeat(frame.get_state().get_repeat());
self.state.set_shuffle(frame.get_state().get_shuffle());
} }
fn load_track(&mut self, play: bool) { fn load_track(&mut self, play: bool) {
@ -678,17 +721,14 @@ struct CommandSender<'a> {
impl<'a> CommandSender<'a> { impl<'a> CommandSender<'a> {
fn new(spirc: &'a mut SpircTask, cmd: MessageType) -> CommandSender { fn new(spirc: &'a mut SpircTask, cmd: MessageType) -> CommandSender {
let frame = protobuf_init!(protocol::spirc::Frame::new(), { let mut frame = protocol::spirc::Frame::new();
version: 1, frame.set_version(1);
protocol_version: "2.0.0", frame.set_protocol_version(::std::convert::Into::into("2.0.0"));
ident: spirc.ident.clone(), frame.set_ident(spirc.ident.clone());
seq_nr: spirc.sequence.get(), frame.set_seq_nr(spirc.sequence.get());
typ: cmd, frame.set_typ(cmd);
frame.set_device_state(spirc.device.clone());
device_state: spirc.device.clone(), frame.set_state_update_id(now_ms());
state_update_id: now_ms(),
});
CommandSender { CommandSender {
spirc: spirc, spirc: spirc,
frame: frame, frame: frame,

View file

@ -1,6 +1,4 @@
use bytes::Bytes; use bytes::Bytes;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use futures::{Async, Future, IntoFuture, Poll, Stream}; use futures::{Async, Future, IntoFuture, Poll, Stream};
use futures::sync::mpsc; use futures::sync::mpsc;
use std::io; use std::io;
@ -45,12 +43,6 @@ static SESSION_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
#[derive(Clone)] #[derive(Clone)]
pub struct Session(Arc<SessionInternal>); pub struct Session(Arc<SessionInternal>);
pub fn device_id(name: &str) -> String {
let mut h = Sha1::new();
h.input_str(name);
h.result_str()
}
impl Session { impl Session {
pub fn connect( pub fn connect(
config: SessionConfig, config: SessionConfig,

View file

@ -2,7 +2,7 @@ use std;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub struct u128 { pub(crate) struct u128 {
high: u64, high: u64,
low: u64, low: u64,
} }

View file

@ -7,7 +7,7 @@ use std::ops::{Mul, Rem, Shr};
mod int128; mod int128;
pub use util::int128::u128; pub(crate) use util::int128::u128;
pub fn rand_vec<G: Rng, R: Rand>(rng: &mut G, size: usize) -> Vec<R> { pub fn rand_vec<G: Rng, R: Rand>(rng: &mut G, size: usize) -> Vec<R> {
rng.gen_iter().take(size).collect() rng.gen_iter().take(size).collect()

View file

@ -5,12 +5,13 @@ use std::env;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use librespot::core::authentication::Credentials; use librespot::core::authentication::Credentials;
use librespot::playback::config::{PlayerConfig, SessionConfig}; use librespot::core::config::SessionConfig;
use librespot::playback::config::PlayerConfig;
use librespot::core::session::Session; use librespot::core::session::Session;
use librespot::core::spotify_id::SpotifyId; use librespot::core::spotify_id::SpotifyId;
use librespot::audio_backend; use librespot::playback::audio_backend;
use librespot::player::Player; use librespot::playback::player::Player;
fn main() { fn main() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();

View file

@ -13,6 +13,7 @@ path = "../metadata"
[dependencies] [dependencies]
futures = "0.1.8" futures = "0.1.8"
log = "0.3.5" log = "0.3.5"
byteorder = "1.2.1"
alsa = { git = "https://github.com/plietar/rust-alsa", optional = true } alsa = { git = "https://github.com/plietar/rust-alsa", optional = true }
portaudio-rs = { version = "0.3.0", optional = true } portaudio-rs = { version = "0.3.0", optional = true }

View file

@ -28,12 +28,16 @@ impl Default for Bitrate {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PlayerConfig { pub struct PlayerConfig {
pub bitrate: Bitrate, pub bitrate: Bitrate,
pub normalisation: bool,
pub normalisation_pregain: f32,
} }
impl Default for PlayerConfig { impl Default for PlayerConfig {
fn default() -> PlayerConfig { fn default() -> PlayerConfig {
PlayerConfig { PlayerConfig {
bitrate: Bitrate::default(), bitrate: Bitrate::default(),
normalisation: false,
normalisation_pregain: 0.0,
} }
} }
} }

View file

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

View file

@ -1,3 +1,4 @@
use byteorder::{LittleEndian, ReadBytesExt};
use futures::sync::oneshot; use futures::sync::oneshot;
use futures::{future, Future}; use futures::{future, Future};
use futures; use futures;
@ -61,6 +62,50 @@ pub enum PlayerEvent {
} }
type PlayerEventChannel = futures::sync::mpsc::UnboundedReceiver<PlayerEvent>; type PlayerEventChannel = futures::sync::mpsc::UnboundedReceiver<PlayerEvent>;
#[derive(Clone, Copy, Debug)]
struct NormalisationData {
track_gain_db: f32,
track_peak: f32,
album_gain_db: f32,
album_peak: f32,
}
impl NormalisationData {
fn parse_from_file<T: Read + Seek>(mut file: T) -> Result<NormalisationData> {
const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144;
file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET)).unwrap();
let track_gain_db = file.read_f32::<LittleEndian>().unwrap();
let track_peak = file.read_f32::<LittleEndian>().unwrap();
let album_gain_db = file.read_f32::<LittleEndian>().unwrap();
let album_peak = file.read_f32::<LittleEndian>().unwrap();
let r = NormalisationData {
track_gain_db: track_gain_db,
track_peak: track_peak,
album_gain_db: album_gain_db,
album_peak: album_peak,
};
Ok(r)
}
fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 {
let mut normalisation_factor = f32::powf(10.0, (data.track_gain_db + config.normalisation_pregain) / 20.0);
if normalisation_factor * data.track_peak > 1.0 {
warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid.");
normalisation_factor = 1.0 / data.track_peak;
}
debug!("Normalisation Data: {:?}", data);
debug!("Applied normalisation factor: {}", normalisation_factor);
normalisation_factor
}
}
impl Player { impl Player {
pub fn new<F>(config: PlayerConfig, session: Session, pub fn new<F>(config: PlayerConfig, session: Session,
audio_filter: Option<Box<AudioFilter + Send>>, audio_filter: Option<Box<AudioFilter + Send>>,
@ -142,11 +187,13 @@ enum PlayerState {
track_id: SpotifyId, track_id: SpotifyId,
decoder: Decoder, decoder: Decoder,
end_of_track: oneshot::Sender<()>, end_of_track: oneshot::Sender<()>,
normalisation_factor: f32,
}, },
Playing { Playing {
track_id: SpotifyId, track_id: SpotifyId,
decoder: Decoder, decoder: Decoder,
end_of_track: oneshot::Sender<()>, end_of_track: oneshot::Sender<()>,
normalisation_factor: f32,
}, },
EndOfTrack { track_id: SpotifyId }, EndOfTrack { track_id: SpotifyId },
Invalid, Invalid,
@ -176,7 +223,7 @@ impl PlayerState {
use self::PlayerState::*; use self::PlayerState::*;
match mem::replace(self, Invalid) { match mem::replace(self, Invalid) {
Playing { track_id, end_of_track, ..} => { Playing { track_id, end_of_track, ..} => {
end_of_track.send(()); let _ = end_of_track.send(());
*self = EndOfTrack { track_id }; *self = EndOfTrack { track_id };
}, },
_ => panic!("Called playing_to_end_of_track in non-playing state.") _ => panic!("Called playing_to_end_of_track in non-playing state.")
@ -186,11 +233,12 @@ impl PlayerState {
fn paused_to_playing(&mut self) { fn paused_to_playing(&mut self) {
use self::PlayerState::*; use self::PlayerState::*;
match ::std::mem::replace(self, Invalid) { match ::std::mem::replace(self, Invalid) {
Paused { decoder, end_of_track, track_id } => { Paused { track_id, decoder, end_of_track, normalisation_factor } => {
*self = Playing { *self = Playing {
track_id: track_id,
decoder: decoder, decoder: decoder,
end_of_track: end_of_track, end_of_track: end_of_track,
track_id: track_id, normalisation_factor: normalisation_factor,
}; };
} }
_ => panic!("invalid state"), _ => panic!("invalid state"),
@ -200,11 +248,12 @@ impl PlayerState {
fn playing_to_paused(&mut self) { fn playing_to_paused(&mut self) {
use self::PlayerState::*; use self::PlayerState::*;
match ::std::mem::replace(self, Invalid) { match ::std::mem::replace(self, Invalid) {
Playing { decoder, end_of_track, track_id } => { Playing { track_id, decoder, end_of_track, normalisation_factor } => {
*self = Paused { *self = Paused {
track_id: track_id,
decoder: decoder, decoder: decoder,
end_of_track: end_of_track, end_of_track: end_of_track,
track_id: track_id, normalisation_factor: normalisation_factor,
}; };
} }
_ => panic!("invalid state"), _ => panic!("invalid state"),
@ -248,14 +297,17 @@ impl PlayerInternal {
} }
if self.sink_running { if self.sink_running {
let packet = if let PlayerState::Playing { ref mut decoder, .. } = self.state { let mut current_normalisation_factor: f32 = 1.0;
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")) Some(decoder.next_packet().expect("Vorbis error"))
} else { } else {
None None
}; };
if let Some(packet) = packet { if let Some(packet) = packet {
self.handle_packet(packet); self.handle_packet(packet, current_normalisation_factor);
} }
} }
} }
@ -279,13 +331,19 @@ impl PlayerInternal {
self.sink_running = false; self.sink_running = false;
} }
fn handle_packet(&mut self, packet: Option<VorbisPacket>) { fn handle_packet(&mut self, packet: Option<VorbisPacket>, normalisation_factor: f32) {
match packet { match packet {
Some(mut packet) => { Some(mut packet) => {
if let Some(ref editor) = self.audio_filter { if let Some(ref editor) = self.audio_filter {
editor.modify_stream(&mut packet.data_mut()) editor.modify_stream(&mut packet.data_mut())
}; };
if self.config.normalisation && normalisation_factor != 1.0 {
for x in packet.data_mut().iter_mut() {
*x = (*x as f32 * normalisation_factor) as i16;
}
}
if let Err(err) = self.sink.write(&packet.data()) { if let Err(err) = self.sink.write(&packet.data()) {
error!("Could not write audio: {}", err); error!("Could not write audio: {}", err);
self.stop_sink(); self.stop_sink();
@ -308,7 +366,7 @@ impl PlayerInternal {
} }
match self.load_track(track_id, position as i64) { match self.load_track(track_id, position as i64) {
Some(decoder) => { Some((decoder, normalisation_factor)) => {
if play { if play {
match self.state { match self.state {
PlayerState::Playing { track_id: old_track_id, ..} PlayerState::Playing { track_id: old_track_id, ..}
@ -326,12 +384,14 @@ impl PlayerInternal {
track_id: track_id, track_id: track_id,
decoder: decoder, decoder: decoder,
end_of_track: end_of_track, end_of_track: end_of_track,
normalisation_factor: normalisation_factor,
}; };
} else { } else {
self.state = PlayerState::Paused { self.state = PlayerState::Paused {
track_id: track_id, track_id: track_id,
decoder: decoder, decoder: decoder,
end_of_track: end_of_track, end_of_track: end_of_track,
normalisation_factor: normalisation_factor,
}; };
match self.state { match self.state {
PlayerState::Playing { track_id: old_track_id, ..} PlayerState::Playing { track_id: old_track_id, ..}
@ -422,7 +482,7 @@ impl PlayerInternal {
} }
} }
fn load_track(&self, track_id: SpotifyId, position: i64) -> Option<Decoder> { fn load_track(&self, track_id: SpotifyId, position: i64) -> Option<(Decoder, f32)> {
let track = Track::get(&self.session, track_id).wait().unwrap(); let track = Track::get(&self.session, track_id).wait().unwrap();
info!("Loading track \"{}\"", track.name); info!("Loading track \"{}\"", track.name);
@ -452,7 +512,18 @@ impl PlayerInternal {
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(); let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap();
let audio_file = Subfile::new(AudioDecrypt::new(key, encrypted_file), 0xa7);
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) {
Ok(normalisation_data) => NormalisationData::get_factor(&self.config, normalisation_data),
Err(_) => {
warn!("Unable to extract normalisation data, using default value.");
1.0 as f32
},
};
let audio_file = Subfile::new(decrypted_file, 0xa7);
let mut decoder = VorbisDecoder::new(audio_file).unwrap(); let mut decoder = VorbisDecoder::new(audio_file).unwrap();
@ -463,7 +534,7 @@ impl PlayerInternal {
info!("Track \"{}\" loaded", track.name); info!("Track \"{}\" loaded", track.name);
Some(decoder) Some((decoder, normalisation_factor))
} }
} }

View file

@ -6,6 +6,7 @@ extern crate librespot;
extern crate tokio_core; extern crate tokio_core;
extern crate tokio_io; extern crate tokio_io;
extern crate tokio_signal; extern crate tokio_signal;
extern crate crypto;
use env_logger::LogBuilder; use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream}; use futures::{Future, Async, Poll, Stream};
@ -18,6 +19,8 @@ use std::str::FromStr;
use tokio_core::reactor::{Handle, Core}; use tokio_core::reactor::{Handle, Core};
use tokio_io::IoStream; use tokio_io::IoStream;
use std::mem; use std::mem;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use librespot::core::authentication::{get_credentials, Credentials}; use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache; use librespot::core::cache::Cache;
@ -35,6 +38,12 @@ use librespot::connect::spirc::{Spirc, SpircTask};
mod player_event_handler; mod player_event_handler;
use player_event_handler::run_program_on_events; use player_event_handler::run_program_on_events;
fn device_id(name: &str) -> String {
let mut h = Sha1::new();
h.input_str(name);
h.result_str()
}
fn usage(program: &str, opts: &getopts::Options) -> String { fn usage(program: &str, opts: &getopts::Options) -> String {
let brief = format!("Usage: {} [options]", program); let brief = format!("Usage: {} [options]", program);
opts.usage(&brief) opts.usage(&brief)
@ -106,7 +115,9 @@ fn setup(args: &[String]) -> Setup {
.optopt("", "device", "Audio device to use. Use '?' to list options if using portaudio", "DEVICE") .optopt("", "device", "Audio device to use. Use '?' to list options if using portaudio", "DEVICE")
.optopt("", "mixer", "Mixer to use", "MIXER") .optopt("", "mixer", "Mixer to use", "MIXER")
.optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME") .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"); .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..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
@ -174,7 +185,7 @@ fn setup(args: &[String]) -> Setup {
}; };
let session_config = { let session_config = {
let device_id = librespot::core::session::device_id(&name); let device_id = device_id(&name);
SessionConfig { SessionConfig {
user_agent: version::version_string(), user_agent: version::version_string(),
@ -187,7 +198,13 @@ fn setup(args: &[String]) -> Setup {
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
.unwrap_or(Bitrate::default()); .unwrap_or(Bitrate::default());
PlayerConfig { bitrate: bitrate } PlayerConfig {
bitrate: bitrate,
normalisation: matches.opt_present("enable-volume-normalisation"),
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 connect_config = {