2017-10-05 18:41:02 +00:00
|
|
|
use super::{Open, Sink};
|
2021-01-07 06:42:38 +00:00
|
|
|
use crate::audio::AudioPacket;
|
2021-03-12 22:05:38 +00:00
|
|
|
use crate::config::AudioFormat;
|
|
|
|
use crate::player::NUM_CHANNELS;
|
2020-11-27 04:55:47 +00:00
|
|
|
use jack::{
|
|
|
|
AsyncClient, AudioOut, Client, ClientOptions, Control, Port, ProcessHandler, ProcessScope,
|
2018-05-17 01:15:17 +00:00
|
|
|
};
|
2021-03-18 21:06:43 +00:00
|
|
|
use std::io;
|
2018-02-26 01:50:41 +00:00
|
|
|
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
2017-10-05 18:41:02 +00:00
|
|
|
|
|
|
|
pub struct JackSink {
|
2021-02-24 20:39:42 +00:00
|
|
|
send: SyncSender<f32>,
|
|
|
|
// We have to keep hold of this object, or the Sink can't play...
|
|
|
|
#[allow(dead_code)]
|
2018-02-26 01:50:41 +00:00
|
|
|
active_client: AsyncClient<(), JackData>,
|
2017-10-05 18:41:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct JackData {
|
2021-02-24 20:39:42 +00:00
|
|
|
rec: Receiver<f32>,
|
2020-11-27 04:55:47 +00:00
|
|
|
port_l: Port<AudioOut>,
|
|
|
|
port_r: Port<AudioOut>,
|
2017-10-05 18:41:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ProcessHandler for JackData {
|
2020-11-27 04:55:47 +00:00
|
|
|
fn process(&mut self, _: &Client, ps: &ProcessScope) -> Control {
|
2017-10-05 18:41:02 +00:00
|
|
|
// get output port buffers
|
2020-11-27 04:55:47 +00:00
|
|
|
let mut out_r = self.port_r.as_mut_slice(ps);
|
|
|
|
let mut out_l = self.port_l.as_mut_slice(ps);
|
2017-10-05 18:41:02 +00:00
|
|
|
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 {
|
2021-02-24 20:39:42 +00:00
|
|
|
buf_r[i] = queue_iter.next().unwrap_or(0.0);
|
|
|
|
buf_l[i] = queue_iter.next().unwrap_or(0.0);
|
2017-10-05 18:41:02 +00:00
|
|
|
}
|
2020-11-27 04:55:47 +00:00
|
|
|
Control::Continue
|
2017-10-05 18:41:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Open for JackSink {
|
2021-03-12 22:05:38 +00:00
|
|
|
fn open(client_name: Option<String>, format: AudioFormat) -> JackSink {
|
|
|
|
if format != AudioFormat::F32 {
|
2021-03-16 19:22:00 +00:00
|
|
|
warn!("JACK currently does not support {:?} output", format);
|
2021-03-12 22:05:38 +00:00
|
|
|
}
|
2021-03-16 19:22:00 +00:00
|
|
|
info!("Using JACK sink with format {:?}", AudioFormat::F32);
|
2017-10-05 18:41:02 +00:00
|
|
|
|
|
|
|
let client_name = client_name.unwrap_or("librespot".to_string());
|
2019-10-08 09:31:18 +00:00
|
|
|
let (client, _status) =
|
2020-11-27 04:55:47 +00:00
|
|
|
Client::new(&client_name[..], ClientOptions::NO_START_SERVER).unwrap();
|
|
|
|
let ch_r = client.register_port("out_0", AudioOut::default()).unwrap();
|
|
|
|
let ch_l = client.register_port("out_1", AudioOut::default()).unwrap();
|
2017-10-05 18:41:02 +00:00
|
|
|
// buffer for samples from librespot (~10ms)
|
2021-03-18 21:06:43 +00:00
|
|
|
let (tx, rx) = sync_channel::<f32>(NUM_CHANNELS as usize * 1024 * format.size());
|
2018-02-26 01:50:41 +00:00
|
|
|
let jack_data = JackData {
|
|
|
|
rec: rx,
|
|
|
|
port_l: ch_l,
|
|
|
|
port_r: ch_r,
|
|
|
|
};
|
2017-10-05 18:41:02 +00:00
|
|
|
let active_client = AsyncClient::new(client, (), jack_data).unwrap();
|
|
|
|
|
2021-03-16 23:00:27 +00:00
|
|
|
Self {
|
2018-02-26 01:50:41 +00:00
|
|
|
send: tx,
|
|
|
|
active_client: active_client,
|
|
|
|
}
|
|
|
|
}
|
2017-10-05 18:41:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Sink for JackSink {
|
2021-03-12 22:05:38 +00:00
|
|
|
start_stop_noop!();
|
2017-10-05 18:41:02 +00:00
|
|
|
|
2021-01-07 06:42:38 +00:00
|
|
|
fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
|
|
|
|
for s in packet.samples().iter() {
|
2017-10-05 18:41:02 +00:00
|
|
|
let res = self.send.send(*s);
|
|
|
|
if res.is_err() {
|
2021-03-18 21:06:43 +00:00
|
|
|
error!("cannot write to channel");
|
2017-10-05 18:41:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|