allow configuring device and delay ms in config

This commit is contained in:
Hailey Somerville 2023-08-20 01:00:55 +10:00
parent e3f3918c15
commit e73796150e
7 changed files with 148 additions and 2 deletions

24
Cargo.lock generated
View file

@ -71,6 +71,7 @@ dependencies = [
"nix 0.26.2",
"rand",
"serde",
"serde_json",
"socket2",
"static_assertions",
"structopt",
@ -352,6 +353,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "jni"
version = "0.19.0"
@ -793,6 +800,12 @@ dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
@ -834,6 +847,17 @@ dependencies = [
"syn 2.0.28",
]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.3"

View file

@ -14,6 +14,7 @@ libc = "0.2.147"
nix = { version = "0.26.2", features = ["time", "socket", "net", "poll", "user", "hostname"], default-features = false }
rand = "0.8.5"
serde = { version = "1.0.183", features = ["derive"] }
serde_json = "1.0.105"
socket2 = "0.5.3"
static_assertions = "1.1.0"
structopt = "0.3.26"

81
src/audio.rs Normal file
View file

@ -0,0 +1,81 @@
use std::process::{Command, Stdio};
use serde::Deserialize;
pub fn set_sink_env(device: &str) {
let Some(index) = find_pulse_node(Kind::Sink, device) else {
eprintln!("falling back to default audio sink");
return;
};
println!("using audio sink at index {}: {}", index.0, device);
std::env::set_var("PULSE_SINK", device);
std::env::set_var("PIPEWIRE_NODE", index.0.to_string());
}
pub fn set_source_env(device: &str) {
let Some(index) = find_pulse_node(Kind::Source, device) else {
eprintln!("falling back to default audio source");
return;
};
println!("using audio source at index {}: {}", index.0, device);
std::env::set_var("PULSE_SOURCE", device);
std::env::set_var("PIPEWIRE_NODE", index.0.to_string());
}
enum Kind {
Source,
Sink,
}
#[derive(Deserialize)]
struct Node {
index: NodeIndex,
name: String,
}
#[derive(Deserialize)]
struct NodeIndex(u64);
fn find_pulse_node(kind: Kind, name: &str) -> Option<NodeIndex> {
let kind = match kind {
Kind::Source => "sources",
Kind::Sink => "sinks",
};
let result = Command::new("pactl")
.args(["--format=json", "list", kind])
.stdout(Stdio::piped())
.output();
let output = match result {
Ok(output) => output,
Err(e) => {
eprintln!("error running pactl to find audio device: {e:?}");
return None;
}
};
let text = match std::str::from_utf8(&output.stdout) {
Ok(text) => text,
Err(e) => {
eprintln!("could not parse pactl output: {e:?}");
return None;
}
};
let nodes = match serde_json::from_str::<Vec<Node>>(text) {
Ok(nodes) => nodes,
Err(e) => {
eprintln!("could not parse pactl output: {e:?}");
return None;
}
};
nodes.into_iter()
.find(|node| node.name == name)
.map(|node| node.index)
}

View file

@ -7,6 +7,21 @@ use serde::Deserialize;
#[derive(Deserialize)]
pub struct Config {
multicast: Option<SocketAddr>,
#[serde(default)]
source: Source,
#[serde(default)]
receive: Receive,
}
#[derive(Deserialize, Default)]
pub struct Source {
device: Option<String>,
delay_ms: Option<u64>,
}
#[derive(Deserialize, Default)]
pub struct Receive {
device: Option<String>,
}
fn set_env_option<T: ToString>(name: &str, value: Option<T>) {
@ -16,7 +31,10 @@ fn set_env_option<T: ToString>(name: &str, value: Option<T>) {
}
pub fn load_into_env(config: &Config) {
set_env_option("BARK_MULTICAST", config.multicast)
set_env_option("BARK_MULTICAST", config.multicast);
set_env_option("BARK_SOURCE_DEVICE", config.source.device.as_ref());
set_env_option("BARK_SOURCE_DELAY_MS", config.source.delay_ms);
set_env_option("BARK_RECEIVE_DEVICE", config.receive.device.as_ref());
}
fn load_file(path: &Path) -> Option<Config> {

View file

@ -1,3 +1,4 @@
mod audio;
mod config;
mod protocol;
mod receive;

View file

@ -472,6 +472,8 @@ impl<T: Copy + Default + Ord> Aggregate<T> {
pub struct ReceiveOpt {
#[structopt(flatten)]
pub socket: SocketOpt,
#[structopt(long, env = "BARK_RECEIVE_DEVICE")]
pub device: Option<String>,
#[structopt(long, default_value="12")]
pub max_seq_gap: usize,
}
@ -480,6 +482,10 @@ pub fn run(opt: ReceiveOpt) -> Result<(), RunError> {
let receiver_id = ReceiverId::generate();
let node = NodeStats::get();
if let Some(device) = &opt.device {
crate::audio::set_sink_env(device);
}
let host = cpal::default_host();
let device = host.default_output_device()

View file

@ -18,13 +18,28 @@ use crate::RunError;
pub struct StreamOpt {
#[structopt(flatten)]
pub socket: SocketOpt,
#[structopt(long, default_value="20")]
#[structopt(
long,
env = "BARK_SOURCE_DEVICE",
)]
pub device: Option<String>,
#[structopt(
long,
env = "BARK_SOURCE_DELAY_MS",
default_value = "20",
)]
pub delay_ms: u64,
}
pub fn run(opt: StreamOpt) -> Result<(), RunError> {
let host = cpal::default_host();
if let Some(device) = &opt.device {
crate::audio::set_source_env(device);
}
let device = host.default_input_device()
.ok_or(RunError::NoDeviceAvailable)?;