free-librespot/playback/src/audio_backend/jackaudio.rs
Brice d40c0f50db Cache volume across restarts (#220)
* create Volume struct for use with Cache

* add "volume" file to Cache

* load cached volume on start, intial overrides cached overrides default

* amend volume_to_mixer function to cache the volume on every change

* pass cache to Spirc and SpircTask so volume_to_mixer has access

* rustfmt changes

* revert volume_to_mixer function and Spirc/SpircTask cache variable

* Volume implements Copy, pass by value instead of reference

* clamp volume to 100 if cached value exceeds limit

* convert Volume to u16 internally, use float and round to convert hex->dec

* convert initial_volume and ConnectConfig.volume to u16 as well

* add cache_volume function to SpircTask

* remove conversion to/from percentage on cached volume

* consolidate device.set_volume, mixer.set_volume, and caching

* streamline intial volume logic
2018-05-17 03:15:17 +02:00

85 lines
2.4 KiB
Rust

use super::{Open, Sink};
use jack::prelude::{
client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port, ProcessHandler,
ProcessScope,
};
use std::io;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
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(())
}
}