Add sink input volume manipulation

This commit is contained in:
GHOSCHT 2024-04-30 23:44:39 +02:00
parent 12b8a4c93b
commit 0a663e97f2
Signed by: ghoscht
GPG key ID: 2C2C1C62A5388E82

View file

@ -3,10 +3,11 @@ extern crate libpulse_binding as pulse;
use pulse::context::{Context, FlagSet as ContextFlagSet};
use pulse::mainloop::threaded::Mainloop;
use pulse::proplist::Proplist;
use pulse::volume;
use regex::Regex;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;
use std::time;
pub fn run() {
@ -18,11 +19,11 @@ pub fn run() {
)
.unwrap();
let mainloop = Rc::new(RefCell::new(
let mainloop = Arc::new(RefCell::new(
Mainloop::new().expect("Failed to create mainloop"),
));
let context = Rc::new(RefCell::new(
let context = Arc::new(RefCell::new(
Context::new_with_proplist(
mainloop.borrow().deref(),
"picoKontrollerContext",
@ -33,8 +34,8 @@ pub fn run() {
// Context state change callback
{
let ml_ref = Rc::clone(&mainloop);
let context_ref = Rc::clone(&context);
let ml_ref = Arc::clone(&mainloop);
let context_ref = Arc::clone(&context);
context
.borrow_mut()
.set_state_callback(Some(Box::new(move || {
@ -82,19 +83,21 @@ pub fn run() {
mainloop.borrow_mut().unlock();
//---------------------
let ml = Rc::clone(&mainloop);
let ctx = Rc::clone(&context);
mute_sink_input(ml, ctx, "Firefox", false);
let ml = Arc::clone(&mainloop);
let ctx = Arc::clone(&context);
set_volume_sink_input(ml, ctx, "Firefox", 100);
//---------------------
loop {
//TODO: react to incoming commands here instead of infinite sleep
std::thread::sleep(time::Duration::from_secs(u64::MAX));
}
}
pub fn set_default_sink(
mainloop: Rc<RefCell<Mainloop>>,
context: Rc<RefCell<Context>>,
mainloop: Arc<RefCell<Mainloop>>,
context: Arc<RefCell<Context>>,
name: &str,
) {
mainloop.borrow_mut().lock();
@ -117,11 +120,11 @@ pub fn set_default_sink(
mainloop.borrow_mut().unlock();
}
pub fn mute_sink_input(
mainloop: Rc<RefCell<Mainloop>>,
context: Rc<RefCell<Context>>,
fn for_all_sink_inputs(
mainloop: Arc<RefCell<Mainloop>>,
context: Arc<RefCell<Context>>,
name: &str,
state: bool,
function: Arc<dyn Fn(Arc<RefCell<Context>>, u32, &mut volume::ChannelVolumes)>,
) {
mainloop.borrow_mut().lock();
let sink_filter = Regex::new(name).expect("Creating RegEx failed");
@ -139,14 +142,73 @@ pub fn mute_sink_input(
println!("{:?}", application_name);
if matches_regex {
println!("Setting default sink to: {}", application_name);
callback_context
.borrow_mut()
.introspect()
.set_sink_input_mute(item.index, state, Option::None);
function(
callback_context.clone(),
item.index,
&mut item.volume.clone(),
);
}
}
_ => {}
});
mainloop.borrow_mut().unlock();
}
pub fn mute_sink_input(
mainloop: Arc<RefCell<Mainloop>>,
context: Arc<RefCell<Context>>,
name: &str,
state: bool,
) {
for_all_sink_inputs(
mainloop,
context,
name,
Arc::new(move |ctx, index, _| {
ctx.borrow_mut()
.introspect()
.set_sink_input_mute(index, state, Option::None);
}),
)
}
pub fn set_volume_sink_input(
mainloop: Arc<RefCell<Mainloop>>,
context: Arc<RefCell<Context>>,
name: &str,
volume: i16,
) {
for_all_sink_inputs(
mainloop,
context,
name,
Arc::new(move |ctx, index, vols| {
let target = percent_to_volume(volume);
for v in vols.get_mut() {
v.0 = target;
}
ctx.borrow_mut()
.introspect()
.set_sink_input_volume(index, vols, Option::None);
}),
)
}
//taken from https://github.com/jantap/rsmixer
pub fn percent_to_volume(target_percent: i16) -> u32 {
let base_delta = (volume::Volume::NORMAL.0 as f32 - volume::Volume::MUTED.0 as f32) / 100.0;
if target_percent < 0 {
volume::Volume::MUTED.0
} else if target_percent == 100 {
volume::Volume::NORMAL.0
} else if target_percent >= 150 {
(volume::Volume::NORMAL.0 as f32 * 1.5) as u32
} else if target_percent < 100 {
volume::Volume::MUTED.0 + target_percent as u32 * base_delta as u32
} else {
volume::Volume::NORMAL.0 + (target_percent - 100) as u32 * base_delta as u32
}
}