mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
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.
This commit is contained in:
parent
8971d3aa68
commit
adeb22b2f3
4 changed files with 91 additions and 0 deletions
|
@ -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"]
|
||||
|
|
79
src/audio_backend/jackaudio.rs
Normal file
79
src/audio_backend/jackaudio.rs
Normal file
|
@ -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<i16>,
|
||||
active_client: AsyncClient<(),JackData>,
|
||||
}
|
||||
|
||||
pub struct JackData {
|
||||
rec: Receiver<i16>,
|
||||
port_l: Port<AudioOutSpec>,
|
||||
port_r: Port<AudioOutSpec>,
|
||||
}
|
||||
|
||||
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<String>) -> 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(())
|
||||
}
|
||||
}
|
|
@ -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::<PortAudioSink>),
|
||||
#[cfg(feature = "pulseaudio-backend")]
|
||||
("pulseaudio", mk_sink::<PulseAudioSink>),
|
||||
#[cfg(feature = "jackaudio-backend")]
|
||||
("jackaudio", mk_sink::<JackSink>),
|
||||
("pipe", mk_sink::<StdoutSink>),
|
||||
];
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue