mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Merge pull request #116 from librespot-org/jackaudio
Jackaudio Support. Closes #93.
This commit is contained in:
commit
977a6db3ef
4 changed files with 87 additions and 0 deletions
|
@ -53,6 +53,7 @@ url = "1.3"
|
||||||
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 }
|
||||||
libpulse-sys = { version = "0.0.0", optional = true }
|
libpulse-sys = { version = "0.0.0", optional = true }
|
||||||
|
jack = { version = "0.5.3", optional = true }
|
||||||
libc = { version = "0.2", optional = true }
|
libc = { version = "0.2", optional = true }
|
||||||
dns-sd = { version = "0.1.3", optional = true }
|
dns-sd = { version = "0.1.3", optional = true }
|
||||||
|
|
||||||
|
@ -65,6 +66,7 @@ protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros", fea
|
||||||
alsa-backend = ["alsa"]
|
alsa-backend = ["alsa"]
|
||||||
portaudio-backend = ["portaudio-rs"]
|
portaudio-backend = ["portaudio-rs"]
|
||||||
pulseaudio-backend = ["libpulse-sys", "libc"]
|
pulseaudio-backend = ["libpulse-sys", "libc"]
|
||||||
|
jackaudio-backend = ["jack"]
|
||||||
|
|
||||||
with-tremor = ["librespot-audio/with-tremor"]
|
with-tremor = ["librespot-audio/with-tremor"]
|
||||||
with-lewton = ["librespot-audio/with-lewton"]
|
with-lewton = ["librespot-audio/with-lewton"]
|
||||||
|
|
75
src/audio_backend/jackaudio.rs
Normal file
75
src/audio_backend/jackaudio.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
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};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
sample as f32 / 32768.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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")]
|
#[cfg(feature = "pulseaudio-backend")]
|
||||||
use self::pulseaudio::PulseAudioSink;
|
use self::pulseaudio::PulseAudioSink;
|
||||||
|
|
||||||
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
|
mod jackaudio;
|
||||||
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
|
use self::jackaudio::JackSink;
|
||||||
|
|
||||||
mod pipe;
|
mod pipe;
|
||||||
use self::pipe::StdoutSink;
|
use self::pipe::StdoutSink;
|
||||||
|
|
||||||
|
@ -41,6 +46,8 @@ pub const BACKENDS : &'static [
|
||||||
("portaudio", mk_sink::<PortAudioSink>),
|
("portaudio", mk_sink::<PortAudioSink>),
|
||||||
#[cfg(feature = "pulseaudio-backend")]
|
#[cfg(feature = "pulseaudio-backend")]
|
||||||
("pulseaudio", mk_sink::<PulseAudioSink>),
|
("pulseaudio", mk_sink::<PulseAudioSink>),
|
||||||
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
|
("jackaudio", mk_sink::<JackSink>),
|
||||||
("pipe", mk_sink::<StdoutSink>),
|
("pipe", mk_sink::<StdoutSink>),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,9 @@ extern crate portaudio_rs;
|
||||||
#[cfg(feature = "libpulse-sys")]
|
#[cfg(feature = "libpulse-sys")]
|
||||||
extern crate libpulse_sys;
|
extern crate libpulse_sys;
|
||||||
|
|
||||||
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
|
extern crate jack;
|
||||||
|
|
||||||
#[cfg(feature = "libc")]
|
#[cfg(feature = "libc")]
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue