From c2672638c95a302e4ab528a189d2d01fbabee6dd Mon Sep 17 00:00:00 2001 From: Maicon Moreira <50086618+Maicon-Moreira@users.noreply.github.com> Date: Wed, 19 Nov 2025 00:10:23 -0300 Subject: [PATCH] Improve audio feedback timing using real sound duration and trimmed WAV files (#349) * feat: add sound duration retrieval for audio feedback * Trim trailing silence from pop_start.wav and pop_stop.wav * refactor: simplify sound file and base directory retrieval * feat: only delay mute if audio feedback is enabled * change the logic * simplify new logic * simplify get_sound_path and get_sound_base_dir, reorder functions * add implicit blocking to playTestSound * modify to my pref style --------- Co-authored-by: CJ Pais --- src-tauri/resources/pop_start.wav | Bin 391758 -> 107870 bytes src-tauri/resources/pop_stop.wav | Bin 391758 -> 101586 bytes src-tauri/src/actions.rs | 19 ++-- src-tauri/src/audio_feedback.rs | 114 +++++++++++++----------- src/components/settings/SoundPicker.tsx | 2 - 5 files changed, 70 insertions(+), 65 deletions(-) diff --git a/src-tauri/resources/pop_start.wav b/src-tauri/resources/pop_start.wav index fe23de930d95024879d0a4a95590fee1b77da33b..86ef9d4fa294886704ec61217072309581186cb4 100644 GIT binary patch delta 58 zcmX^2Mf~0=HufM-H@C2*j1$>)C3zVb7<>}T(##C?%=HWn3>X+v5=#;VmNITXn84J? O(tM6(`#BcI2@3!io)T{W delta 45170 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 diff --git a/src-tauri/resources/pop_stop.wav b/src-tauri/resources/pop_stop.wav index 04024adfdd7b50f84e5b516d73ee3c7f9bb887a6..7b9376dae531ac8730a59b53c400c87688cdafc7 100644 GIT binary patch delta 65 zcmV-H0KWgu^cT{Z1_x3}Mn=kv0g(qS84Umc08C+aW;QV{H!d+SFaQ8#VRT`Hi~+Y% X`vI8(ll}q_kzNRc{sOoD0s+*Cwni39 delta 27490 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 path.to_path_buf(), - Err(e) => { - error!( - "Failed to resolve audio file path '{}': {}", - resource_path, e - ); - return; - } - }; - - let settings = settings::get_settings(&app_handle); - let selected_device = settings.selected_output_device.clone(); - - if let Err(e) = play_audio_file(&audio_path, selected_device, volume) { - error!("Failed to play sound '{}': {}", resource_path, e); - } - }); +fn resolve_sound_path( + app: &AppHandle, + settings: &AppSettings, + sound_type: SoundType, +) -> Option { + let sound_file = get_sound_path(settings, sound_type); + let base_dir = get_sound_base_dir(settings); + app.path().resolve(&sound_file, base_dir).ok() } -fn get_sound_path(app: &AppHandle, sound_type: SoundType) -> String { - let settings = settings::get_settings(app); - match sound_type { - SoundType::Start => match settings.sound_theme { - crate::settings::SoundTheme::Custom => "custom_start.wav".to_string(), - _ => settings.sound_theme.to_start_path(), - }, - SoundType::Stop => match settings.sound_theme { - crate::settings::SoundTheme::Custom => "custom_stop.wav".to_string(), - _ => settings.sound_theme.to_stop_path(), - }, +fn get_sound_path(settings: &AppSettings, sound_type: SoundType) -> String { + match (settings.sound_theme, sound_type) { + (SoundTheme::Custom, SoundType::Start) => "custom_start.wav".to_string(), + (SoundTheme::Custom, SoundType::Stop) => "custom_stop.wav".to_string(), + (_, SoundType::Start) => settings.sound_theme.to_start_path(), + (_, SoundType::Stop) => settings.sound_theme.to_stop_path(), + } +} + +fn get_sound_base_dir(settings: &AppSettings) -> tauri::path::BaseDirectory { + match settings.sound_theme { + SoundTheme::Custom => tauri::path::BaseDirectory::AppData, + _ => tauri::path::BaseDirectory::Resource, } } pub fn play_feedback_sound(app: &AppHandle, sound_type: SoundType) { - // Only play if audio feedback is enabled let settings = settings::get_settings(app); if !settings.audio_feedback { return; } + if let Some(path) = resolve_sound_path(app, &settings, sound_type) { + play_sound_async(app, path); + } +} - let sound_file = get_sound_path(app, sound_type); - let base_dir = if settings.sound_theme == crate::settings::SoundTheme::Custom { - tauri::path::BaseDirectory::AppData - } else { - tauri::path::BaseDirectory::Resource - }; - play_sound(app, &sound_file, base_dir); +pub fn play_feedback_sound_blocking(app: &AppHandle, sound_type: SoundType) { + let settings = settings::get_settings(app); + if !settings.audio_feedback { + return; + } + if let Some(path) = resolve_sound_path(app, &settings, sound_type) { + play_sound_blocking(app, &path); + } } pub fn play_test_sound(app: &AppHandle, sound_type: SoundType) { - // Always play test sound, regardless of audio_feedback setting let settings = settings::get_settings(app); - let sound_file = get_sound_path(app, sound_type); - let base_dir = if settings.sound_theme == crate::settings::SoundTheme::Custom { - tauri::path::BaseDirectory::AppData - } else { - tauri::path::BaseDirectory::Resource - }; - play_sound(app, &sound_file, base_dir); + if let Some(path) = resolve_sound_path(app, &settings, sound_type) { + play_sound_async(app, path); + } +} + +fn play_sound_async(app: &AppHandle, path: PathBuf) { + let app_handle = app.clone(); + thread::spawn(move || { + if let Err(e) = play_sound_at_path(&app_handle, path.as_path()) { + error!("Failed to play sound '{}': {}", path.display(), e); + } + }); +} + +fn play_sound_blocking(app: &AppHandle, path: &Path) { + if let Err(e) = play_sound_at_path(app, path) { + error!("Failed to play sound '{}': {}", path.display(), e); + } +} + +fn play_sound_at_path(app: &AppHandle, path: &Path) -> Result<(), Box> { + let settings = settings::get_settings(app); + let volume = settings.audio_feedback_volume; + let selected_device = settings.selected_output_device.clone(); + play_audio_file(path, selected_device, volume) } fn play_audio_file( diff --git a/src/components/settings/SoundPicker.tsx b/src/components/settings/SoundPicker.tsx index 629f737..3c35832 100644 --- a/src/components/settings/SoundPicker.tsx +++ b/src/components/settings/SoundPicker.tsx @@ -33,8 +33,6 @@ export const SoundPicker: React.FC = ({ const handlePlayBothSounds = async () => { await playTestSound("start"); - // Wait before playing stop sound - await new Promise((resolve) => setTimeout(resolve, 800)); await playTestSound("stop"); };