Compare commits

...

29 Commits

Author SHA1 Message Date
arabianq dc61342af8 change version to 1.3.2 2026-01-16 16:19:17 +03:00
arabianq fded7f7b3f deps: cargo update 2026-01-16 16:18:44 +03:00
arabianq ec83680333 deps: bump rfd to 0.17.2 2026-01-16 16:18:11 +03:00
arabianq 16e94e71d3 fix: update input device properties to output for capture_MONO 2026-01-16 16:16:27 +03:00
arabianq 56040934e9 cargo update 2026-01-08 02:56:37 +03:00
arabianq 89975cb124 change version to 1.3.1 2026-01-08 02:53:50 +03:00
arabianq 03e20ffa7e deps: bump rfd to 0.17.1 2026-01-08 02:53:16 +03:00
arabianq 8f7ea09ef5 deps: bump clap to 4.5.54 2026-01-08 02:49:54 +03:00
arabianq c639721f2a deps: bump serde_json to 1.0.149 2026-01-08 02:49:11 +03:00
arabianq 84512efda8 deps: bump tokio to 1.49.0 2026-01-08 02:48:23 +03:00
arabianq 6cf4a9744d replace println! with eprintln! for errors 2026-01-08 02:46:44 +03:00
arabianq d9ced4e650 fix: replace .except() with correct error handling 2026-01-08 02:45:07 +03:00
arabianq c1f3f34169 feat: pwsp-gui now uses Request::toggle_loop instead of set Request::set_loop 2026-01-02 04:41:00 +03:00
arabianq 6c06da7b0d feat: implemented toggle-loop 2026-01-02 04:39:48 +03:00
arabianq 023caa4616 change version to 1.3.0 2026-01-02 04:33:00 +03:00
arabianq fc593f8a8e cargo update 2026-01-02 04:24:32 +03:00
arabianq f281ad5a78 deps: bump serde_json to 1.0.148 2026-01-02 04:18:58 +03:00
arabianq fe8fec4ea7 fix: loop -> tokio::select! in pwsp-daemon main loop for less CPU usage 2026-01-02 04:12:49 +03:00
arabianq 7583e72687 fix: remove debug print statements from commands loop 2026-01-02 04:09:21 +03:00
arabianq e4b4a3d198 fix: remove unnecessary mutability in audio player lock 2026-01-02 03:59:56 +03:00
arabianq a7af1caace fix: highly improved GUI performance 2026-01-02 03:59:05 +03:00
arabianq c5086189d6 fix: changed position slider step from 1.0 to 0.01
now it looks fine when playing short files
2026-01-02 03:58:38 +03:00
arabianq ac667f1273 feat: make_request_sync now uses tokio instead of futures 2026-01-02 03:33:20 +03:00
arabianq fdb79e609f fix: deadlock in play_toggle 2026-01-02 03:32:56 +03:00
arabianq 531a49dbdf feat: implemented loop support in pwsp-gui 2026-01-02 03:18:51 +03:00
arabianq 2a13c25230 feat: implemented loop support in pwsp-cli 2026-01-02 02:57:34 +03:00
arabianq 408f5317c4 feat: implemented loop functionality on daemon level 2026-01-02 02:25:30 +03:00
arabianq 3672d6f43e feat: now pwsp-daemon main loop is separated into commands_loop and player_loop 2026-01-02 02:10:58 +03:00
arabianq cc3bec0746 fix: change get_state and get_position methods to take immutable reference 2026-01-02 02:09:10 +03:00
15 changed files with 377 additions and 777 deletions
Generated
+131 -711
View File
File diff suppressed because it is too large Load Diff
+5 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "pwsp" name = "pwsp"
version = "1.2.0" version = "1.3.2"
edition = "2024" edition = "2024"
authors = ["arabian"] authors = ["arabian"]
description = "PWSP lets you play audio files through your microphone. Has both CLI and GUI clients." description = "PWSP lets you play audio files through your microphone. Has both CLI and GUI clients."
@@ -12,19 +12,18 @@ keywords = ["soundpad", "pipewire", "linux", "cli", "gui"]
[dependencies] [dependencies]
tokio = { version = "1.48.0", features = ["full"] } tokio = { version = "1.49.0", features = ["full"] }
futures = { version = "0.3.31", features = ["thread-pool"] }
async-trait = "0.1.89" async-trait = "0.1.89"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145" serde_json = "1.0.149"
clap = { version = "4.5.53", default-features = false, features = ["std", "suggestions", "help", "usage", "error-context", "derive"] } clap = { version = "4.5.54", default-features = false, features = ["std", "suggestions", "help", "usage", "error-context", "derive"] }
dirs = "6.0.0" dirs = "6.0.0"
rodio = { version = "0.21.1", default-features = false, features = ["symphonia-all", "playback"] } rodio = { version = "0.21.1", default-features = false, features = ["symphonia-all", "playback"] }
pipewire = "0.9.2" pipewire = "0.9.2"
rfd = "0.16.0" rfd = { version = "0.17.2", default-features = false, features = ["xdg-portal"]}
egui = { version = "0.33.3", default-features = false, features = ["default_fonts", "rayon"] } egui = { version = "0.33.3", default-features = false, features = ["default_fonts", "rayon"] }
eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] } eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] }
+1 -1
View File
@@ -4,7 +4,7 @@
%global cargo_install_lib 0 %global cargo_install_lib 0
Name: pwsp Name: pwsp
Version: 1.2.0 Version: 1.3.2
Release: %autorelease Release: %autorelease
Summary: Lets you play audio files through your microphone Summary: Lets you play audio files through your microphone
+12 -1
View File
@@ -45,6 +45,8 @@ enum Actions {
Stop, Stop,
/// Play a file /// Play a file
Play { file_path: PathBuf }, Play { file_path: PathBuf },
/// Toggle loop
ToggleLoop,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
@@ -65,6 +67,8 @@ enum GetCommands {
Input, Input,
/// All audio inputs /// All audio inputs
Inputs, Inputs,
/// Is loop enabled (true or false)
Loop,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
@@ -75,6 +79,8 @@ enum SetCommands {
Position { position: f32 }, Position { position: f32 },
/// Audio input id (see pwsp-cli get inputs) /// Audio input id (see pwsp-cli get inputs)
Input { name: String }, Input { name: String },
/// Enable or disable loop (true or false)
Loop { enabled: String },
} }
#[tokio::main] #[tokio::main]
@@ -91,6 +97,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
Actions::TogglePause => Request::toggle_pause(), Actions::TogglePause => Request::toggle_pause(),
Actions::Stop => Request::stop(), Actions::Stop => Request::stop(),
Actions::Play { file_path } => Request::play(file_path.to_str().unwrap()), Actions::Play { file_path } => Request::play(file_path.to_str().unwrap()),
Actions::ToggleLoop => Request::toggle_loop(),
}, },
Commands::Get { parameter } => match parameter { Commands::Get { parameter } => match parameter {
GetCommands::IsPaused => Request::get_is_paused(), GetCommands::IsPaused => Request::get_is_paused(),
@@ -101,15 +108,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
GetCommands::CurrentFilePath => Request::get_current_file_path(), GetCommands::CurrentFilePath => Request::get_current_file_path(),
GetCommands::Input => Request::get_input(), GetCommands::Input => Request::get_input(),
GetCommands::Inputs => Request::get_inputs(), GetCommands::Inputs => Request::get_inputs(),
GetCommands::Loop => Request::get_loop(),
}, },
Commands::Set { parameter } => match parameter { Commands::Set { parameter } => match parameter {
SetCommands::Volume { volume } => Request::set_volume(volume), SetCommands::Volume { volume } => Request::set_volume(volume),
SetCommands::Position { position } => Request::seek(position), SetCommands::Position { position } => Request::seek(position),
SetCommands::Input { name } => Request::set_input(&name), SetCommands::Input { name } => Request::set_input(&name),
SetCommands::Loop { enabled } => Request::set_loop(&enabled),
}, },
}; };
let response = make_request(request).await?; let response = make_request(request)
.await
.map_err(|e| e as Box<dyn Error>)?;
println!("{} : {}", response.status, response.message); println!("{} : {}", response.status, response.message);
Ok(()) Ok(())
+48 -4
View File
@@ -1,5 +1,8 @@
use pwsp::{ use pwsp::{
types::socket::{Request, Response}, types::{
audio_player::PlayerState,
socket::{Request, Response},
},
utils::{ utils::{
commands::parse_command, commands::parse_command,
daemon::{ daemon::{
@@ -9,10 +12,11 @@ use pwsp::{
pipewire::create_virtual_mic, pipewire::create_virtual_mic,
}, },
}; };
use std::{error::Error, fs}; use std::{error::Error, fs, time::Duration};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::UnixListener, net::UnixListener,
time::sleep,
}; };
#[tokio::main] #[tokio::main]
@@ -44,6 +48,27 @@ async fn main() -> Result<(), Box<dyn Error>> {
socket_path.to_str().unwrap_or_default() socket_path.to_str().unwrap_or_default()
); );
let commands_loop_handle = tokio::spawn(async {
commands_loop(listener).await.ok();
});
let player_loop_handle = tokio::spawn(async {
player_loop().await;
});
tokio::select! {
_ = commands_loop_handle => {
eprint!("Commands loop was finished, stopping program...");
}
_ = player_loop_handle => {
eprint!("Audio Player loop was finished, stopping program...");
}
}
Ok(())
}
async fn commands_loop(listener: UnixListener) -> Result<(), Box<dyn Error>> {
loop { loop {
let (mut stream, _addr) = listener.accept().await?; let (mut stream, _addr) = listener.accept().await?;
@@ -64,7 +89,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
} }
let request: Request = serde_json::from_slice(&buffer).unwrap(); let request: Request = serde_json::from_slice(&buffer).unwrap();
println!("Received request: {:?}", request);
// ---------- Read request (end) ---------- // ---------- Read request (end) ----------
// ---------- Generate response (start) ---------- // ---------- Generate response (start) ----------
@@ -89,8 +113,28 @@ async fn main() -> Result<(), Box<dyn Error>> {
eprintln!("Failed to write response to client!"); eprintln!("Failed to write response to client!");
return; return;
} }
println!("Sent response: {:?}", response);
// ---------- Send response (end) ---------- // ---------- Send response (end) ----------
}); });
} }
} }
async fn player_loop() {
loop {
let mut audio_player = get_audio_player().await.lock().await;
// Start playback again if loop is enabled
let should_play = audio_player.get_state() == PlayerState::Stopped
&& audio_player.current_file_path.is_some()
&& audio_player.looped;
if should_play {
let file_path = audio_player.current_file_path.clone().unwrap();
audio_player
.play(&file_path)
.await
.expect("Something went wrong while trying to play the file");
}
sleep(Duration::from_millis(100)).await;
}
}
+18 -2
View File
@@ -108,13 +108,29 @@ impl SoundpadGui {
} }
// -------------------------------- // --------------------------------
// ---------- Loop Button ----------
let loop_button = Button::new(
RichText::new(match self.audio_player_state.looped {
true => icons::ICON_REPEAT_ONE,
false => icons::ICON_REPEAT,
})
.size(18.0),
)
.frame(false);
let loop_button_response = ui.add_sized([15.0, 30.0], loop_button);
if loop_button_response.clicked() {
self.toggle_loop();
}
// --------------------------------
// ---------- Position Slider ---------- // ---------- Position Slider ----------
let position_slider = Slider::new( let position_slider = Slider::new(
&mut self.app_state.position_slider_value, &mut self.app_state.position_slider_value,
0.0..=self.audio_player_state.duration, 0.0..=self.audio_player_state.duration,
) )
.show_value(false) .show_value(false)
.step_by(1.0); .step_by(0.01);
let default_slider_width = ui.spacing().slider_width; let default_slider_width = ui.spacing().slider_width;
let position_slider_width = ui.available_width() let position_slider_width = ui.available_width()
@@ -150,7 +166,7 @@ impl SoundpadGui {
icons::ICON_VOLUME_DOWN icons::ICON_VOLUME_DOWN
}; };
let volume_icon = Label::new(RichText::new(volume_icon).size(18.0)); let volume_icon = Label::new(RichText::new(volume_icon).size(18.0));
ui.add_sized([30.0, 25.0], volume_icon); ui.add_sized([30.0, 30.0], volume_icon);
// -------------------------------- // --------------------------------
// ---------- Volume Slider ---------- // ---------- Volume Slider ----------
+20 -12
View File
@@ -56,20 +56,24 @@ impl SoundpadGui {
} }
pub fn play_toggle(&mut self) { pub fn play_toggle(&mut self) {
let mut guard = self.audio_player_state_shared.lock().unwrap(); let (new_state, request) = {
guard.state = match guard.state { let guard = self.audio_player_state_shared.lock().unwrap();
PlayerState::Playing => { match guard.state {
make_request_sync(Request::pause()).ok(); PlayerState::Playing => (Some(PlayerState::Paused), Some(Request::pause())),
guard.new_state = Some(PlayerState::Paused); PlayerState::Paused => (Some(PlayerState::Playing), Some(Request::resume())),
PlayerState::Paused PlayerState::Stopped => (None, None),
} }
PlayerState::Paused => {
make_request_sync(Request::resume()).ok();
guard.new_state = Some(PlayerState::Playing);
PlayerState::Playing
}
PlayerState::Stopped => PlayerState::Stopped,
}; };
if let Some(req) = request {
make_request_sync(req).ok();
}
if let Some(state) = new_state {
let mut guard = self.audio_player_state_shared.lock().unwrap();
guard.new_state = Some(state.clone());
guard.state = state;
}
} }
pub fn open_file(&mut self) { pub fn open_file(&mut self) {
@@ -125,6 +129,10 @@ impl SoundpadGui {
daemon_config.save_to_file().ok(); daemon_config.save_to_file().ok();
} }
} }
pub fn toggle_loop(&mut self) {
make_request_sync(Request::toggle_loop()).ok();
}
} }
pub async fn run() -> Result<(), Box<dyn Error>> { pub async fn run() -> Result<(), Box<dyn Error>> {
+28 -23
View File
@@ -33,6 +33,8 @@ pub struct AudioPlayer {
pub duration: Option<f32>, pub duration: Option<f32>,
pub current_file_path: Option<PathBuf>, pub current_file_path: Option<PathBuf>,
pub looped: bool,
} }
impl AudioPlayer { impl AudioPlayer {
@@ -62,6 +64,8 @@ impl AudioPlayer {
duration: None, duration: None,
current_file_path: None, current_file_path: None,
looped: false,
}; };
if default_input_device.is_some() { if default_input_device.is_some() {
@@ -75,7 +79,7 @@ impl AudioPlayer {
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"),
Err(_) => println!("Failed to send terminate signal to link thread"), Err(_) => eprintln!("Failed to send terminate signal to link thread"),
} }
} }
} }
@@ -84,7 +88,7 @@ impl AudioPlayer {
self.abort_link_thread(); self.abort_link_thread();
if self.current_input_device.is_none() { if self.current_input_device.is_none() {
println!("No input device selected, skipping device linking"); eprintln!("No input device selected, skipping device linking");
return Ok(()); return Ok(());
} }
@@ -99,29 +103,30 @@ impl AudioPlayer {
} }
if pwsp_daemon_input.is_none() { if pwsp_daemon_input.is_none() {
println!("Could not find pwsp-daemon input device, skipping device linking"); eprintln!("Could not find pwsp-daemon input device, skipping device linking");
return Ok(()); return Ok(());
} }
let pwsp_daemon_input = pwsp_daemon_input.unwrap(); let pwsp_daemon_input = pwsp_daemon_input.unwrap();
let current_input_device = self.current_input_device.clone().unwrap(); let current_input_device = self.current_input_device.clone().unwrap();
let output_fl = current_input_device
.clone() let Some(output_fl) = current_input_device.output_fl.clone() else {
.output_fl eprintln!("Failed to get pwsp-daemon output_fl");
.expect("Failed to get pwsp-daemon output_fl"); return Ok(());
let output_fr = current_input_device };
.clone() let Some(output_fr) = current_input_device.output_fr.clone() else {
.output_fr eprintln!("Failed to get pwsp-daemon output_fr");
.expect("Failed to get pwsp-daemon output_fl"); return Ok(());
let input_fl = pwsp_daemon_input };
.clone() let Some(input_fl) = pwsp_daemon_input.input_fl.clone() else {
.input_fl eprintln!("Failed to get pwsp-daemon input_fl");
.expect("Failed to get pwsp-daemon input_fl"); return Ok(());
let input_fr = pwsp_daemon_input };
.clone() let Some(input_fr) = pwsp_daemon_input.input_fr.clone() else {
.input_fr eprintln!("Failed to get pwsp-daemon input_fr");
.expect("Failed to get pwsp-daemon input_fr"); return Ok(());
};
self.input_link_sender = Some(create_link(output_fl, output_fr, input_fl, input_fr)?); self.input_link_sender = Some(create_link(output_fl, output_fr, input_fl, input_fr)?);
Ok(()) Ok(())
@@ -147,7 +152,7 @@ impl AudioPlayer {
self.sink.is_paused() self.sink.is_paused()
} }
pub fn get_state(&mut self) -> PlayerState { pub fn get_state(&self) -> PlayerState {
if self.sink.len() == 0 { if self.sink.len() == 0 {
return PlayerState::Stopped; return PlayerState::Stopped;
} }
@@ -164,7 +169,7 @@ impl AudioPlayer {
self.sink.set_volume(volume); self.sink.set_volume(volume);
} }
pub fn get_position(&mut self) -> f32 { pub fn get_position(&self) -> f32 {
if self.get_state() == PlayerState::Stopped { if self.get_state() == PlayerState::Stopped {
return 0.0; return 0.0;
} }
@@ -222,7 +227,7 @@ impl AudioPlayer {
} }
pub fn get_current_file_path(&mut self) -> &Option<PathBuf> { pub fn get_current_file_path(&mut self) -> &Option<PathBuf> {
if self.get_state() == PlayerState::Stopped { if self.get_state() == PlayerState::Stopped && !self.looped {
self.current_file_path = None; self.current_file_path = None;
} }
&self.current_file_path &self.current_file_path
+42 -2
View File
@@ -52,6 +52,14 @@ pub struct SetCurrentInputCommand {
pub name: Option<String>, pub name: Option<String>,
} }
pub struct GetLoopCommand {}
pub struct SetLoopCommand {
pub enabled: Option<bool>,
}
pub struct ToggleLoopCommand {}
#[async_trait] #[async_trait]
impl Executable for PingCommand { impl Executable for PingCommand {
async fn execute(&self) -> Response { async fn execute(&self) -> Response {
@@ -117,7 +125,7 @@ impl Executable for IsPausedCommand {
#[async_trait] #[async_trait]
impl Executable for GetStateCommand { impl Executable for GetStateCommand {
async fn execute(&self) -> Response { async fn execute(&self) -> Response {
let mut audio_player = get_audio_player().await.lock().await; let audio_player = get_audio_player().await.lock().await;
let state = audio_player.get_state(); let state = audio_player.get_state();
Response::new(true, serde_json::to_string(&state).unwrap()) Response::new(true, serde_json::to_string(&state).unwrap())
} }
@@ -148,7 +156,7 @@ impl Executable for SetVolumeCommand {
#[async_trait] #[async_trait]
impl Executable for GetPositionCommand { impl Executable for GetPositionCommand {
async fn execute(&self) -> Response { async fn execute(&self) -> Response {
let mut audio_player = get_audio_player().await.lock().await; let audio_player = get_audio_player().await.lock().await;
let position = audio_player.get_position(); let position = audio_player.get_position();
Response::new(true, position.to_string()) Response::new(true, position.to_string())
} }
@@ -256,3 +264,35 @@ impl Executable for SetCurrentInputCommand {
} }
} }
} }
#[async_trait]
impl Executable for GetLoopCommand {
async fn execute(&self) -> Response {
let audio_player = get_audio_player().await.lock().await;
Response::new(true, audio_player.looped.to_string())
}
}
#[async_trait]
impl Executable for SetLoopCommand {
async fn execute(&self) -> Response {
let mut audio_player = get_audio_player().await.lock().await;
match self.enabled {
Some(enabled) => {
audio_player.looped = enabled;
Response::new(true, format!("Loop was set to {}", enabled))
}
None => Response::new(false, "Invalid enabled value"),
}
}
}
#[async_trait]
impl Executable for ToggleLoopCommand {
async fn execute(&self) -> Response {
let mut audio_player = get_audio_player().await.lock().await;
audio_player.looped = !audio_player.looped;
Response::new(true, format!("Loop was set to {}", audio_player.looped))
}
}
+1
View File
@@ -36,6 +36,7 @@ pub struct AudioPlayerState {
pub current_file_path: PathBuf, pub current_file_path: PathBuf,
pub is_paused: bool, pub is_paused: bool,
pub looped: bool,
pub volume: f32, pub volume: f32,
pub new_volume: Option<f32>, pub new_volume: Option<f32>,
+12
View File
@@ -87,6 +87,18 @@ impl Request {
pub fn set_input(name: &str) -> Self { pub fn set_input(name: &str) -> Self {
Request::new("set_input", vec![("input_name", name)]) Request::new("set_input", vec![("input_name", name)])
} }
pub fn get_loop() -> Self {
Request::new("get_loop", vec![])
}
pub fn set_loop(enabled: &str) -> Self {
Request::new("set_loop", vec![("enabled", enabled)])
}
pub fn toggle_loop() -> Self {
Request::new("toggle_loop", vec![])
}
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
+11
View File
@@ -48,6 +48,17 @@ pub fn parse_command(request: &Request) -> Option<Box<dyn Executable + Send>> {
let name = Some(request.args.get("input_name").unwrap_or(&String::new())).cloned(); let name = Some(request.args.get("input_name").unwrap_or(&String::new())).cloned();
Some(Box::new(SetCurrentInputCommand { name })) Some(Box::new(SetCurrentInputCommand { name }))
} }
"get_loop" => Some(Box::new(GetLoopCommand {})),
"set_loop" => {
let enabled = request
.args
.get("enabled")
.unwrap_or(&String::new())
.parse::<bool>()
.ok();
Some(Box::new(SetLoopCommand { enabled }))
}
"toggle_loop" => Some(Box::new(ToggleLoopCommand {})),
_ => None, _ => None,
} }
} }
+3 -3
View File
@@ -47,7 +47,7 @@ pub async fn link_player_to_virtual_mic() -> Result<(), Box<dyn Error>> {
} }
if pwsp_daemon_output.is_none() { if pwsp_daemon_output.is_none() {
println!("Could not find pwsp-daemon output device, skipping device linking"); eprintln!("Could not find pwsp-daemon output device, skipping device linking");
return Ok(()); return Ok(());
} }
@@ -60,7 +60,7 @@ pub async fn link_player_to_virtual_mic() -> Result<(), Box<dyn Error>> {
} }
if pwsp_daemon_input.is_none() { if pwsp_daemon_input.is_none() {
println!("Could not find pwsp-daemon input device, skipping device linking"); eprintln!("Could not find pwsp-daemon input device, skipping device linking");
return Ok(()); return Ok(());
} }
@@ -124,7 +124,7 @@ pub async fn wait_for_daemon() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub async fn make_request(request: Request) -> Result<Response, Box<dyn Error>> { pub async fn make_request(request: Request) -> Result<Response, Box<dyn Error + Send + Sync>> {
let socket_path = get_runtime_dir().join("daemon.sock"); let socket_path = get_runtime_dir().join("daemon.sock");
let mut stream = UnixStream::connect(socket_path).await?; let mut stream = UnixStream::connect(socket_path).await?;
+43 -10
View File
@@ -24,7 +24,11 @@ pub fn get_gui_config() -> GuiConfig {
} }
pub fn make_request_sync(request: Request) -> Result<Response, Box<dyn Error>> { pub fn make_request_sync(request: Request) -> Result<Response, Box<dyn Error>> {
futures::executor::block_on(make_request(request)) tokio::task::block_in_place(|| {
tokio::runtime::Handle::current()
.block_on(make_request(request))
.map_err(|e| e as Box<dyn Error>)
})
} }
pub fn format_time_pair(position: f32, duration: f32) -> String { pub fn format_time_pair(position: f32, duration: f32) -> String {
@@ -40,7 +44,7 @@ pub fn format_time_pair(position: f32, duration: f32) -> String {
pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerState>>) { pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerState>>) {
tokio::spawn(async move { tokio::spawn(async move {
let sleep_duration = Duration::from_millis(100); let sleep_duration = Duration::from_secs_f32(1.0 / 60.0);
loop { loop {
wait_for_daemon().await.ok(); wait_for_daemon().await.ok();
@@ -53,15 +57,39 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
let duration_req = Request::get_duration(); let duration_req = Request::get_duration();
let current_input_req = Request::get_input(); let current_input_req = Request::get_input();
let all_inputs_req = Request::get_inputs(); let all_inputs_req = Request::get_inputs();
let looped_req = Request::get_loop();
let state_res = make_request(state_req).await.unwrap_or_default(); let (
let file_path_res = make_request(file_path_req).await.unwrap_or_default(); state_res,
let is_paused_res = make_request(is_paused_req).await.unwrap_or_default(); file_path_res,
let volume_res = make_request(volume_req).await.unwrap_or_default(); is_paused_res,
let position_res = make_request(position_req).await.unwrap_or_default(); volume_res,
let duration_res = make_request(duration_req).await.unwrap_or_default(); position_res,
let current_input_res = make_request(current_input_req).await.unwrap_or_default(); duration_res,
let all_inputs_res = make_request(all_inputs_req).await.unwrap_or_default(); current_input_res,
all_inputs_res,
looped_res,
) = tokio::join!(
make_request(state_req),
make_request(file_path_req),
make_request(is_paused_req),
make_request(volume_req),
make_request(position_req),
make_request(duration_req),
make_request(current_input_req),
make_request(all_inputs_req),
make_request(looped_req),
);
let state_res = state_res.unwrap_or_default();
let file_path_res = file_path_res.unwrap_or_default();
let is_paused_res = is_paused_res.unwrap_or_default();
let volume_res = volume_res.unwrap_or_default();
let position_res = position_res.unwrap_or_default();
let duration_res = duration_res.unwrap_or_default();
let current_input_res = current_input_res.unwrap_or_default();
let all_inputs_res = all_inputs_res.unwrap_or_default();
let looped_res = looped_res.unwrap_or_default();
let state = match state_res.status { let state = match state_res.status {
true => serde_json::from_str::<PlayerState>(&state_res.message).unwrap(), true => serde_json::from_str::<PlayerState>(&state_res.message).unwrap(),
@@ -116,6 +144,10 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
.collect::<HashMap<String, String>>(), .collect::<HashMap<String, String>>(),
false => HashMap::new(), false => HashMap::new(),
}; };
let looped = match looped_res.status {
true => looped_res.message.parse::<bool>().unwrap_or_default(),
false => false,
};
{ {
let mut guard = audio_player_state_shared.lock().unwrap(); let mut guard = audio_player_state_shared.lock().unwrap();
@@ -146,6 +178,7 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
guard.duration = if duration > 0.0 { duration } else { 1.0 }; guard.duration = if duration > 0.0 { duration } else { 1.0 };
guard.current_input = current_input; guard.current_input = current_input;
guard.all_inputs = all_inputs; guard.all_inputs = all_inputs;
guard.looped = looped;
} }
sleep(sleep_duration).await; sleep(sleep_duration).await;
+2 -2
View File
@@ -165,8 +165,8 @@ pub async fn get_all_devices() -> Result<(Vec<AudioDevice>, Vec<AudioDevice>), B
input_device.input_fr = Some(port) input_device.input_fr = Some(port)
} }
"capture_MONO" => { "capture_MONO" => {
input_device.input_fl = Some(port.clone()); input_device.output_fl = Some(port.clone());
input_device.input_fr = Some(port); input_device.output_fr = Some(port);
} }
_ => {} _ => {}
} }