mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-28 06:21:23 +00:00
feat: now pwsp will automatically detect when input device is connected/disconnected and properly link/unlink it
This commit is contained in:
+48
-34
@@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
types::pipewire::{AudioDevice, DeviceType, Terminate},
|
types::pipewire::{DeviceType, Terminate},
|
||||||
utils::{
|
utils::{
|
||||||
daemon::get_daemon_config,
|
daemon::get_daemon_config,
|
||||||
pipewire::{create_link, get_all_devices, get_device},
|
pipewire::{create_link, get_device},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink, Source};
|
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink, Source};
|
||||||
@@ -49,7 +49,7 @@ pub struct AudioPlayer {
|
|||||||
pub next_id: u32,
|
pub next_id: u32,
|
||||||
|
|
||||||
input_link_sender: Option<pipewire::channel::Sender<Terminate>>,
|
input_link_sender: Option<pipewire::channel::Sender<Terminate>>,
|
||||||
pub current_input_device: Option<AudioDevice>,
|
pub input_device_name: Option<String>,
|
||||||
|
|
||||||
pub volume: f32, // Master volume
|
pub volume: f32, // Master volume
|
||||||
}
|
}
|
||||||
@@ -58,13 +58,6 @@ impl AudioPlayer {
|
|||||||
pub async fn new() -> Result<Self, Box<dyn Error>> {
|
pub async fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
let daemon_config = get_daemon_config();
|
let daemon_config = get_daemon_config();
|
||||||
let default_volume = daemon_config.default_volume.unwrap_or(1.0);
|
let default_volume = daemon_config.default_volume.unwrap_or(1.0);
|
||||||
let mut default_input_device: Option<AudioDevice> = None;
|
|
||||||
if let Some(name) = daemon_config.default_input_name
|
|
||||||
&& let Ok(device) = get_device(&name).await
|
|
||||||
&& device.device_type == DeviceType::Input
|
|
||||||
{
|
|
||||||
default_input_device = Some(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
let stream_handle = OutputStreamBuilder::open_default_stream()?;
|
let stream_handle = OutputStreamBuilder::open_default_stream()?;
|
||||||
|
|
||||||
@@ -74,12 +67,12 @@ impl AudioPlayer {
|
|||||||
next_id: 1,
|
next_id: 1,
|
||||||
|
|
||||||
input_link_sender: None,
|
input_link_sender: None,
|
||||||
current_input_device: default_input_device.clone(),
|
input_device_name: daemon_config.default_input_name.clone(),
|
||||||
|
|
||||||
volume: default_volume,
|
volume: default_volume,
|
||||||
};
|
};
|
||||||
|
|
||||||
if default_input_device.is_some() {
|
if audio_player.input_device_name.is_some() {
|
||||||
audio_player.link_devices().await?;
|
audio_player.link_devices().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +82,10 @@ impl AudioPlayer {
|
|||||||
fn abort_link_thread(&mut self) {
|
fn abort_link_thread(&mut self) {
|
||||||
if let Some(sender) = &self.input_link_sender {
|
if let Some(sender) = &self.input_link_sender {
|
||||||
match sender.send(Terminate {}) {
|
match sender.send(Terminate {}) {
|
||||||
Ok(_) => println!("Sent terminate signal to link thread"),
|
Ok(_) => {
|
||||||
|
println!("Sent terminate signal to link thread");
|
||||||
|
self.input_link_sender = None;
|
||||||
|
}
|
||||||
Err(_) => eprintln!("Failed to send terminate signal to link thread"),
|
Err(_) => eprintln!("Failed to send terminate signal to link thread"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,42 +94,43 @@ impl AudioPlayer {
|
|||||||
async fn link_devices(&mut self) -> Result<(), Box<dyn Error>> {
|
async fn link_devices(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
self.abort_link_thread();
|
self.abort_link_thread();
|
||||||
|
|
||||||
if self.current_input_device.is_none() {
|
let input_device;
|
||||||
|
if let Some(input_device_name) = &self.input_device_name {
|
||||||
|
if let Ok(device) = get_device(input_device_name).await {
|
||||||
|
input_device = device;
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"Could not find selected input device {}, skipping device linking",
|
||||||
|
input_device_name
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
eprintln!("No input device selected, skipping device linking");
|
eprintln!("No input device selected, skipping device linking");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (input_devices, _) = get_all_devices().await?;
|
let daemon_input;
|
||||||
|
if let Ok(device) = get_device("pwsp-virtual-mic").await {
|
||||||
let mut pwsp_daemon_input: Option<AudioDevice> = None;
|
daemon_input = device;
|
||||||
for input_device in input_devices {
|
} else {
|
||||||
if input_device.name == "pwsp-virtual-mic" {
|
eprintln!("Could not find pwsp-virtual-mic device, skipping device linking");
|
||||||
pwsp_daemon_input = Some(input_device);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if pwsp_daemon_input.is_none() {
|
|
||||||
eprintln!("Could not find pwsp-daemon input device, skipping device linking");
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let pwsp_daemon_input = pwsp_daemon_input.unwrap();
|
let Some(output_fl) = input_device.output_fl.clone() else {
|
||||||
let current_input_device = self.current_input_device.clone().unwrap();
|
|
||||||
|
|
||||||
let Some(output_fl) = current_input_device.output_fl.clone() else {
|
|
||||||
eprintln!("Failed to get pwsp-daemon output_fl");
|
eprintln!("Failed to get pwsp-daemon output_fl");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let Some(output_fr) = current_input_device.output_fr.clone() else {
|
let Some(output_fr) = input_device.output_fr.clone() else {
|
||||||
eprintln!("Failed to get pwsp-daemon output_fr");
|
eprintln!("Failed to get pwsp-daemon output_fr");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let Some(input_fl) = pwsp_daemon_input.input_fl.clone() else {
|
let Some(input_fl) = daemon_input.input_fl.clone() else {
|
||||||
eprintln!("Failed to get pwsp-daemon input_fl");
|
eprintln!("Failed to get pwsp-daemon input_fl");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let Some(input_fr) = pwsp_daemon_input.input_fr.clone() else {
|
let Some(input_fr) = daemon_input.input_fr.clone() else {
|
||||||
eprintln!("Failed to get pwsp-daemon input_fr");
|
eprintln!("Failed to get pwsp-daemon input_fr");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
@@ -333,6 +330,23 @@ impl AudioPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update(&mut self) {
|
pub async fn update(&mut self) {
|
||||||
|
if let Some(input_device_name) = &self.input_device_name {
|
||||||
|
// Unlink devices if selected input device was removed
|
||||||
|
if self.input_link_sender.is_some() && get_device(input_device_name).await.is_err() {
|
||||||
|
// Selected input device was removed
|
||||||
|
eprintln!(
|
||||||
|
"Selected input device {} was removed, unlinking devices",
|
||||||
|
input_device_name
|
||||||
|
);
|
||||||
|
self.abort_link_thread();
|
||||||
|
}
|
||||||
|
// Link devices if not linked
|
||||||
|
else if self.input_link_sender.is_none() {
|
||||||
|
self.link_devices().await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle looped sounds
|
||||||
let mut restarts = vec![];
|
let mut restarts = vec![];
|
||||||
|
|
||||||
for (id, sound) in &self.tracks {
|
for (id, sound) in &self.tracks {
|
||||||
@@ -363,7 +377,7 @@ impl AudioPlayer {
|
|||||||
return Err("Selected device is not an input device".into());
|
return Err("Selected device is not an input device".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current_input_device = Some(input_device);
|
self.input_device_name = Some(name.to_string());
|
||||||
|
|
||||||
self.link_devices().await?;
|
self.link_devices().await?;
|
||||||
|
|
||||||
|
|||||||
+13
-6
@@ -1,6 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
types::{audio_player::PlayerState, socket::Response},
|
types::{audio_player::PlayerState, socket::Response},
|
||||||
utils::{daemon::get_audio_player, pipewire::get_all_devices},
|
utils::{
|
||||||
|
daemon::get_audio_player,
|
||||||
|
pipewire::{get_all_devices, get_device},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -254,11 +257,15 @@ impl Executable for GetTracksCommand {
|
|||||||
impl Executable for GetCurrentInputCommand {
|
impl Executable for GetCurrentInputCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = get_audio_player().await.lock().await;
|
||||||
if let Some(input_device) = &audio_player.current_input_device {
|
if let Some(input_device_name) = &audio_player.input_device_name {
|
||||||
Response::new(
|
if let Ok(input_device) = get_device(input_device_name).await {
|
||||||
true,
|
Response::new(
|
||||||
format!("{} - {}", input_device.name, input_device.nick),
|
true,
|
||||||
)
|
format!("{} - {}", input_device.name, input_device.nick),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Response::new(false, "Failed to get current input device")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Response::new(false, "No input device selected")
|
Response::new(false, "No input device selected")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user