From adeb22b2f31e904568b92c1d103b7681ed08dda7 Mon Sep 17 00:00:00 2001 From: loblik Date: Thu, 5 Oct 2017 20:41:02 +0200 Subject: [PATCH 1/3] add support for jack audio connection kit This is initial support for JACK. It creates ports at startup and keeps it connected while librespot is running. So when librespot playback is stoped it writes silence (zeroes). It uses jack crate (rust-jack) which needs libjack. To compile in jack support use --features jackaudio-backend. And run librespot with --backend jackaudio. --- Cargo.toml | 2 + src/audio_backend/jackaudio.rs | 79 ++++++++++++++++++++++++++++++++++ src/audio_backend/mod.rs | 7 +++ src/lib.rs | 3 ++ 4 files changed, 91 insertions(+) create mode 100644 src/audio_backend/jackaudio.rs diff --git a/Cargo.toml b/Cargo.toml index f4e63498..c2a8d87f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ url = "1.3" alsa = { git = "https://github.com/plietar/rust-alsa", optional = true } portaudio-rs = { version = "0.3.0", optional = true } libpulse-sys = { version = "0.0.0", optional = true } +jack = { version = "0.5.3", optional = true } [build-dependencies] rand = "0.3.13" @@ -62,6 +63,7 @@ protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros", fea alsa-backend = ["alsa"] portaudio-backend = ["portaudio-rs"] pulseaudio-backend = ["libpulse-sys"] +jackaudio-backend = ["jack"] with-tremor = ["librespot-audio/with-tremor"] with-lewton = ["librespot-audio/with-lewton"] diff --git a/src/audio_backend/jackaudio.rs b/src/audio_backend/jackaudio.rs new file mode 100644 index 00000000..e1a67aec --- /dev/null +++ b/src/audio_backend/jackaudio.rs @@ -0,0 +1,79 @@ +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}; + +#[allow(dead_code)] +pub struct JackSink { + send: SyncSender, + active_client: AsyncClient<(),JackData>, +} + +pub struct JackData { + rec: Receiver, + port_l: Port, + port_r: Port, +} + +fn pcm_to_f32(sample: i16) -> f32 { + let mut f: f32 = sample as f32 / 32768.0; + if f > 1.0 { f = 1.0; } + if f < -1.0 { f = -1.0; } + f +} + +impl ProcessHandler for JackData { + fn process(&mut self, _: &Client, ps: &ProcessScope) -> JackControl { + // get output port buffers + let mut out_r = AudioOutPort::new(&mut self.port_r, ps); + let mut out_l = AudioOutPort::new(&mut self.port_l, ps); + let buf_r: &mut [f32] = &mut out_r; + let buf_l: &mut [f32] = &mut out_l; + // get queue iterator + let mut queue_iter = self.rec.try_iter(); + + let buf_size = buf_r.len(); + for i in 0..buf_size { + buf_r[i] = pcm_to_f32(queue_iter.next().unwrap_or(0)); + buf_l[i] = pcm_to_f32(queue_iter.next().unwrap_or(0)); + } + JackControl::Continue + } +} + +impl Open for JackSink { + fn open(client_name: Option) -> JackSink { + info!("Using jack sink!"); + + 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(); + // 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 active_client = AsyncClient::new(client, (), jack_data).unwrap(); + + JackSink { send: tx, active_client: active_client } + } +} + +impl Sink for JackSink { + fn start(&mut self) -> io::Result<()> { + Ok(()) + } + + fn stop(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write(&mut self, data: &[i16]) -> io::Result<()> { + for s in data.iter() { + let res = self.send.send(*s); + if res.is_err() { + error!("jackaudio: cannot write to channel"); + } + } + Ok(()) + } +} diff --git a/src/audio_backend/mod.rs b/src/audio_backend/mod.rs index 1effc05a..be73bf6c 100644 --- a/src/audio_backend/mod.rs +++ b/src/audio_backend/mod.rs @@ -29,6 +29,11 @@ mod pulseaudio; #[cfg(feature = "pulseaudio-backend")] use self::pulseaudio::PulseAudioSink; +#[cfg(feature = "jackaudio-backend")] +mod jackaudio; +#[cfg(feature = "jackaudio-backend")] +use self::jackaudio::JackSink; + mod pipe; use self::pipe::StdoutSink; @@ -41,6 +46,8 @@ pub const BACKENDS : &'static [ ("portaudio", mk_sink::), #[cfg(feature = "pulseaudio-backend")] ("pulseaudio", mk_sink::), + #[cfg(feature = "jackaudio-backend")] + ("jackaudio", mk_sink::), ("pipe", mk_sink::), ]; diff --git a/src/lib.rs b/src/lib.rs index b9c920ec..5a9274b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,9 @@ extern crate portaudio_rs; #[cfg(feature = "libpulse-sys")] extern crate libpulse_sys; +#[cfg(feature = "jackaudio-backend")] +extern crate jack; + pub mod audio_backend; pub mod discovery; pub mod keymaster; From f35f52cbf94548e11b0ad059d3cb754af18c5ec8 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Fri, 2 Feb 2018 05:03:59 +0100 Subject: [PATCH 2/3] Remove redundant code --- src/audio_backend/jackaudio.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/audio_backend/jackaudio.rs b/src/audio_backend/jackaudio.rs index e1a67aec..9b389ea6 100644 --- a/src/audio_backend/jackaudio.rs +++ b/src/audio_backend/jackaudio.rs @@ -3,7 +3,6 @@ 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}; -#[allow(dead_code)] pub struct JackSink { send: SyncSender, active_client: AsyncClient<(),JackData>, @@ -16,10 +15,7 @@ pub struct JackData { } fn pcm_to_f32(sample: i16) -> f32 { - let mut f: f32 = sample as f32 / 32768.0; - if f > 1.0 { f = 1.0; } - if f < -1.0 { f = -1.0; } - f + sample as f32 / 32768.0; } impl ProcessHandler for JackData { From b22f252abd7ff0782cea62eaf91c81f4e38e0a3e Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Fri, 2 Feb 2018 05:14:00 +0100 Subject: [PATCH 3/3] Add missing " --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1796bc43..6e3a798d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros", fea [features] alsa-backend = ["alsa"] portaudio-backend = ["portaudio-rs"] -pulseaudio-backend = ["libpulse-sys, "libc"] +pulseaudio-backend = ["libpulse-sys", "libc"] jackaudio-backend = ["jack"] with-tremor = ["librespot-audio/with-tremor"]