Files
wldash/src/modules/sound.rs
T

538 lines
18 KiB
Rust
Raw Normal View History

2019-08-01 19:51:06 +02:00
use crate::buffer::Buffer;
2019-08-28 18:04:31 +02:00
use crate::cmd::Cmd;
2019-08-01 19:51:06 +02:00
use crate::color::Color;
2019-08-06 14:04:34 +02:00
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
2019-08-11 00:52:47 +02:00
use crate::modules::module::{Input, ModuleImpl};
2019-08-01 19:51:06 +02:00
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use chrono::{DateTime, Local};
use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::{
flags, introspect::ServerInfo, introspect::SinkInfo, subscribe::subscription_masks,
subscribe::Facility, subscribe::Operation as SubscribeOperation, Context, State as PulseState,
};
use libpulse_binding::mainloop::standard::IterateResult;
use libpulse_binding::mainloop::standard::Mainloop;
use libpulse_binding::proplist::{properties, Proplist};
2019-08-29 01:30:36 +02:00
use libpulse_binding::volume::{ChannelVolumes, Volume, VOLUME_MAX, VOLUME_NORM};
2019-08-01 19:51:06 +02:00
struct PulseAudioConnection {
mainloop: Rc<RefCell<Mainloop>>,
context: Rc<RefCell<Context>>,
}
#[derive(Clone)]
struct PulseAudioSinkInfo {
volume: ChannelVolumes,
mute: bool,
}
struct PulseAudioClient {
sender: Sender<PulseAudioClientRequest>,
default_sink: String,
sinks: HashMap<String, PulseAudioSinkInfo>,
}
enum PulseAudioClientRequest {
GetDefaultDevice(Option<Sender<bool>>),
GetSinkInfoByIndex(Option<Sender<bool>>, u32),
GetSinkInfoByName(Option<Sender<bool>>, String),
SetSinkVolumeByName(Option<Sender<bool>>, String, ChannelVolumes),
SetSinkMuteByName(Option<Sender<bool>>, String, bool),
}
2019-08-28 18:04:31 +02:00
#[derive(Debug)]
struct PulseAudioSoundDeviceInner {
2019-08-01 19:51:06 +02:00
name: Option<String>,
volume: Option<ChannelVolumes>,
2019-08-04 14:11:21 +02:00
volume_avg: f32,
2019-08-01 19:51:06 +02:00
muted: bool,
2019-08-28 18:04:31 +02:00
default_sink: String,
}
struct PulseAudioSoundDevice {
client: Arc<Mutex<PulseAudioClient>>,
inner: Arc<Mutex<PulseAudioSoundDeviceInner>>,
2019-08-01 19:51:06 +02:00
}
impl PulseAudioConnection {
fn new() -> Result<Self, ::std::io::Error> {
let mut proplist = Proplist::new().unwrap();
proplist
2019-08-29 01:30:36 +02:00
.set_str(properties::APPLICATION_NAME, "wldash")
2019-08-01 19:51:06 +02:00
.unwrap();
let mainloop = Rc::new(RefCell::new(Mainloop::new().unwrap()));
let context = Rc::new(RefCell::new(
Context::new_with_proplist(mainloop.borrow().deref(), "wldash_context", &proplist)
.unwrap(),
));
context
.borrow_mut()
.connect(None, flags::NOFLAGS, None)
.map_err(|_e| {
::std::io::Error::new(
::std::io::ErrorKind::Other,
"unable to connect to pulseaudio context",
)
})?;
let mut connection = PulseAudioConnection { mainloop, context };
// Wait for context to be ready
loop {
connection.iterate(false)?;
match connection.context.borrow().get_state() {
PulseState::Ready => {
break;
}
PulseState::Failed | PulseState::Terminated => {}
_ => {}
}
}
Ok(connection)
}
fn iterate(&mut self, blocking: bool) -> Result<(), ::std::io::Error> {
match self.mainloop.borrow_mut().iterate(blocking) {
IterateResult::Quit(_) | IterateResult::Err(_) => Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
"unable to iterate pulseaudio state",
)),
IterateResult::Success(_) => Ok(()),
}
}
}
impl PulseAudioClient {
2019-08-28 18:04:31 +02:00
fn new<F>(listener: F) -> Result<Arc<Mutex<Self>>, ::std::io::Error>
where
F: Fn(Arc<Mutex<Self>>) -> (),
F: Send + 'static + Clone,
{
2019-08-01 19:51:06 +02:00
let (tx, rx) = channel();
let client = Arc::new(Mutex::new(PulseAudioClient {
sender: tx,
default_sink: "@DEFAULT_SINK@".to_string(),
sinks: HashMap::new(),
}));
2019-08-28 18:04:31 +02:00
let loop_client = client.clone();
2019-08-01 19:51:06 +02:00
let (tx1, rx1) = channel();
2019-08-28 18:04:31 +02:00
let _ = thread::Builder::new()
.name("pa_writer".to_string())
.spawn(move || {
let mut conn = match PulseAudioConnection::new() {
Ok(v) => {
tx1.send(true).unwrap();
v
}
Err(_) => {
tx1.send(false).unwrap();
return;
}
};
// make sure mainloop dispatched everything
for _ in 0..10 {
conn.iterate(false).unwrap();
2019-08-01 19:51:06 +02:00
}
2019-08-28 18:04:31 +02:00
loop {
let cl = loop_client.clone();
let l = listener.clone();
match rx.recv() {
Err(_) => return,
Ok(req) => {
let mut introspector = conn.context.borrow_mut().introspect();
2019-08-01 19:51:06 +02:00
2019-08-28 18:04:31 +02:00
match req {
PulseAudioClientRequest::GetDefaultDevice(s) => {
introspector.get_server_info(move |info| {
let _res = PulseAudioClient::server_info_callback(
cl.clone(),
l.clone(),
info,
);
if let Some(s) = &s {
let _ = s.send(true);
}
});
2019-08-01 19:51:06 +02:00
}
2019-08-28 18:04:31 +02:00
PulseAudioClientRequest::GetSinkInfoByIndex(s, index) => {
introspector.get_sink_info_by_index(index, move |res| {
PulseAudioClient::sink_info_callback(
cl.clone(),
l.clone(),
res,
);
if let Some(s) = &s {
let _ = s.send(true);
}
});
2019-08-01 19:51:06 +02:00
}
2019-08-28 18:04:31 +02:00
PulseAudioClientRequest::GetSinkInfoByName(s, name) => {
introspector.get_sink_info_by_name(&name, move |res| {
PulseAudioClient::sink_info_callback(
cl.clone(),
l.clone(),
res,
);
if let Some(s) = &s {
let _ = s.send(true);
}
});
}
PulseAudioClientRequest::SetSinkVolumeByName(s, name, volumes) => {
introspector.set_sink_volume_by_name(&name, &volumes, None);
if let Some(s) = &s {
let _ = s.send(true);
}
}
PulseAudioClientRequest::SetSinkMuteByName(s, name, mute) => {
introspector.set_sink_mute_by_name(&name, mute, None);
if let Some(s) = &s {
let _ = s.send(true);
}
}
};
2019-08-01 19:51:06 +02:00
2019-08-28 18:04:31 +02:00
// send request and receive response
conn.iterate(true).unwrap();
conn.iterate(true).unwrap();
}
2019-08-01 19:51:06 +02:00
}
}
2019-08-28 18:04:31 +02:00
});
2019-08-01 19:51:06 +02:00
// subscribe
let cl2 = client.clone();
let (tx2, rx2) = channel();
2019-08-28 18:04:31 +02:00
let _ = thread::Builder::new()
.name("pa_reader".to_string())
.spawn(move || {
let conn = match PulseAudioConnection::new() {
Ok(v) => {
tx2.send(true).unwrap();
v
}
Err(_) => {
tx2.send(false).unwrap();
return;
}
};
// subcribe for events
conn.context
.borrow_mut()
.set_subscribe_callback(Some(Box::new(move |facility, operation, index| {
cl2.lock()
.unwrap()
.subscribe_callback(facility, operation, index)
})));
conn.context.borrow_mut().subscribe(
subscription_masks::SERVER | subscription_masks::SINK,
|_| {},
);
2019-08-01 19:51:06 +02:00
2019-08-28 18:04:31 +02:00
conn.mainloop.borrow_mut().run().unwrap();
});
2019-08-02 10:57:22 +02:00
if !rx1.recv().unwrap() || !rx2.recv().unwrap() {
2019-08-01 19:51:06 +02:00
return Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
"unable to start pulseaudio thread",
));
}
Ok(client)
}
fn send(&self, request: PulseAudioClientRequest) -> Result<(), ::std::io::Error> {
let res = self.sender.send(request).map_err(|_e| {
::std::io::Error::new(
::std::io::ErrorKind::Other,
"unable to send pulseaudio request",
)
});
res
}
2019-08-28 18:04:31 +02:00
fn server_info_callback<F>(s: Arc<Mutex<Self>>, listener: F, server_info: &ServerInfo)
where
F: Fn(Arc<Mutex<Self>>) -> (),
F: Send + 'static,
{
2019-08-01 19:51:06 +02:00
match server_info.default_sink_name.clone() {
None => {}
Some(default_sink) => {
2019-08-28 18:04:31 +02:00
(*s.lock().unwrap()).default_sink = default_sink.into();
listener(s);
2019-08-01 19:51:06 +02:00
}
}
}
2019-08-28 18:04:31 +02:00
fn sink_info_callback<F>(s: Arc<Mutex<Self>>, listener: F, result: ListResult<&SinkInfo>)
where
F: Fn(Arc<Mutex<Self>>) -> (),
F: Send + 'static,
{
2019-08-01 19:51:06 +02:00
match result {
ListResult::End | ListResult::Error => {}
2019-08-04 14:23:49 +02:00
ListResult::Item(sink_info) => match sink_info.name.clone() {
None => {}
Some(name) => {
let info = PulseAudioSinkInfo {
volume: sink_info.volume,
mute: sink_info.mute,
};
2019-08-28 18:04:31 +02:00
s.lock().unwrap().sinks.insert(name.into(), info);
listener(s);
2019-08-01 19:51:06 +02:00
}
2019-08-04 14:23:49 +02:00
},
2019-08-01 19:51:06 +02:00
}
}
fn subscribe_callback(
&self,
facility: Option<Facility>,
_operation: Option<SubscribeOperation>,
index: u32,
) {
match facility {
None => {}
Some(facility) => match facility {
Facility::Server => {
let _ = self.send(PulseAudioClientRequest::GetDefaultDevice(None));
}
Facility::Sink => {
let _ = self.send(PulseAudioClientRequest::GetSinkInfoByIndex(None, index));
}
_ => {}
},
}
}
}
impl PulseAudioSoundDevice {
2019-08-28 18:04:31 +02:00
fn new<F>(listener: F) -> Result<Self, ::std::io::Error>
where
F: Fn() -> (),
F: Send + 'static + Clone,
{
let inner = Arc::new(Mutex::new(PulseAudioSoundDeviceInner {
name: None,
volume: None,
volume_avg: 0.0,
muted: false,
default_sink: "@DEFAULT_SINK@".to_string(),
}));
let cb_inner = inner.clone();
let client = PulseAudioClient::new(move |client| {
let mut inner = cb_inner.lock().unwrap();
inner.default_sink = client.lock().unwrap().default_sink.clone();
let name = inner
.name
.clone()
.unwrap_or_else(|| inner.default_sink.clone());
let sink_info = match client.lock().unwrap().sinks.get(&name) {
None => return,
Some(sink_info) => (*sink_info).clone(),
};
inner.volume = Some(sink_info.volume);
inner.volume_avg = sink_info.volume.avg().0 as f32 / VOLUME_NORM.0 as f32;
inner.muted = sink_info.mute;
listener();
})?;
2019-08-01 19:51:06 +02:00
let cl = client.clone();
let (tx, rx) = channel();
{
let cl = client.lock().unwrap();
cl.send(PulseAudioClientRequest::GetDefaultDevice(Some(tx)))?;
}
rx.recv().unwrap();
let name = {
let cl = client.lock().unwrap();
cl.default_sink.to_string()
};
2019-08-28 18:04:31 +02:00
(*inner.lock().unwrap()).name = Some(name.clone());
let device = PulseAudioSoundDevice {
2019-08-01 19:51:06 +02:00
client: cl,
2019-08-28 18:04:31 +02:00
inner: inner,
};
2019-08-01 19:51:06 +02:00
let (tx, rx) = channel();
{
let cl = client.lock().unwrap();
cl.send(PulseAudioClientRequest::GetSinkInfoByName(Some(tx), name))?;
}
rx.recv().unwrap();
Ok(device)
}
2019-08-04 14:11:21 +02:00
fn volume(&self) -> f32 {
2019-08-28 18:04:31 +02:00
let inner = self.inner.lock().unwrap();
inner.volume_avg
2019-08-01 19:51:06 +02:00
}
fn set_volume(&mut self, step: f32) -> Result<(), ::std::io::Error> {
2019-08-28 18:04:31 +02:00
let mut inner = self.inner.lock().unwrap();
let mut volume = match inner.volume {
2019-08-01 19:51:06 +02:00
Some(volume) => volume,
None => {
return Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
"unable to set volume",
))
}
};
// apply step to volumes
2019-08-04 14:11:21 +02:00
let step = (step * VOLUME_NORM.0 as f32).round() as i32;
2019-08-29 01:30:36 +02:00
if step > 0 {
volume.inc_clamp(Volume(step as u32), VOLUME_MAX);
} else {
volume.decrease(Volume(-step as u32));
2019-08-01 19:51:06 +02:00
}
2019-08-28 18:04:31 +02:00
let name = inner
.name
.clone()
.unwrap_or_else(|| inner.default_sink.clone());
2019-08-01 19:51:06 +02:00
// update volumes
2019-08-28 18:04:31 +02:00
inner.volume = Some(volume);
inner.volume_avg = volume.avg().0 as f32 / VOLUME_NORM.0 as f32;
2019-08-01 19:51:06 +02:00
self.client
.lock()
.unwrap()
.send(PulseAudioClientRequest::SetSinkVolumeByName(
2019-08-28 18:04:31 +02:00
None, name, volume,
2019-08-01 19:51:06 +02:00
))?;
Ok(())
}
fn toggle(&mut self) -> Result<(), ::std::io::Error> {
2019-08-28 18:04:31 +02:00
let mut inner = self.inner.lock().unwrap();
inner.muted = !inner.muted;
2019-08-01 19:51:06 +02:00
self.client
.lock()
.unwrap()
.send(PulseAudioClientRequest::SetSinkMuteByName(
None,
2019-08-28 18:04:31 +02:00
inner
.name
.clone()
.unwrap_or_else(|| inner.default_sink.clone()),
inner.muted,
2019-08-01 19:51:06 +02:00
))?;
Ok(())
}
}
pub struct PulseAudio {
2019-08-28 18:04:31 +02:00
device: PulseAudioSoundDevice,
2019-08-06 16:39:17 +02:00
font: Font,
2019-08-05 23:15:36 +02:00
dirty: Arc<Mutex<bool>>,
2019-08-01 19:51:06 +02:00
}
impl PulseAudio {
2019-08-27 15:07:39 +02:00
pub fn new(listener: Sender<Cmd>) -> Result<PulseAudio, ::std::io::Error> {
2019-08-06 16:39:17 +02:00
let mut font = Font::new(&ROBOTO_REGULAR, 24.0);
font.add_str_to_cache("volume");
2019-08-28 18:04:31 +02:00
let dirty = Arc::new(Mutex::new(true));
let dev_dirty = dirty.clone();
let device = PulseAudioSoundDevice::new(move || {
*dev_dirty.lock().unwrap() = true;
2019-08-27 15:07:39 +02:00
listener.send(Cmd::Draw).unwrap();
2019-08-28 18:04:31 +02:00
})?;
let pa = PulseAudio {
device: device,
font: font,
dirty: dirty,
};
2019-08-01 19:51:06 +02:00
Ok(pa)
}
}
2019-08-05 23:15:36 +02:00
impl ModuleImpl for PulseAudio {
2019-08-01 19:51:06 +02:00
fn draw(
&self,
buf: &mut Buffer,
bg: &Color,
_time: &DateTime<Local>,
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
2019-08-28 18:04:31 +02:00
let muted = self.device.inner.lock().unwrap().muted;
let mut vol = self.device.volume();
2019-08-01 19:51:06 +02:00
buf.memset(bg);
2019-08-02 10:57:22 +02:00
let c = if muted {
Color::new(1.0, 1.0, 0.0, 1.0)
} else {
Color::new(1.0, 1.0, 1.0, 1.0)
};
2019-08-06 16:39:17 +02:00
self.font.draw_text(
buf,
2019-08-01 19:51:06 +02:00
bg,
&Color::new(1.0, 1.0, 1.0, 1.0),
"volume",
)?;
draw_bar(&mut buf.offset((128, 0))?, &c, 464, 24, vol)?;
2019-08-02 10:57:22 +02:00
let mut iter = 1.0;
while vol > 1.0 {
let c = &Color::new(0.75 / iter, 0.25 / iter, 0.25 / iter, 1.0);
vol -= 1.0;
iter += 1.0;
draw_bar(&mut buf.offset((128, 0))?, &c, 464, 24, vol)?;
2019-08-01 19:51:06 +02:00
}
draw_box(&mut buf.offset((128, 0))?, &c, (464, 24))?;
2019-08-01 19:51:06 +02:00
Ok(vec![buf.get_signed_bounds()])
}
fn update(&mut self, _time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
2019-08-05 23:15:36 +02:00
let mut d = self.dirty.lock().unwrap();
if *d || force {
*d = false;
2019-08-01 19:51:06 +02:00
Ok(true)
} else {
Ok(false)
}
}
fn input(&mut self, input: Input) {
match input {
Input::Scroll {
pos: _pos,
x: _x,
y,
} => {
2019-08-28 18:04:31 +02:00
self.device.set_volume(y as f32 / -800.0).unwrap();
*self.dirty.lock().unwrap() = true;
2019-08-01 19:51:06 +02:00
}
Input::Click { pos: _pos, button } => match button {
273 => {
2019-08-28 18:04:31 +02:00
self.device.toggle().unwrap();
*self.dirty.lock().unwrap() = true;
2019-08-01 19:51:06 +02:00
}
_ => {}
},
2019-08-02 15:59:10 +02:00
_ => {}
2019-08-01 19:51:06 +02:00
}
}
}