Skip to content

Commit 5b1872a

Browse files
committed
Merge branch 'main' of github.com:cjpais/Handy
2 parents 631a565 + b80d908 commit 5b1872a

15 files changed

Lines changed: 236 additions & 10 deletions

File tree

src-tauri/Cargo.lock

Lines changed: 110 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ tauri-plugin-store = "2"
4242
tauri-plugin-os = "2"
4343
enigo = "0.5.0"
4444
tauri-plugin-process = "2"
45+
rodio = "0.20.1"
4546

4647
[target.'cfg(target_os = "macos")'.dependencies]
4748
whisper-rs = { version = "0.13.2", features = ["metal"] }

src-tauri/resources/default_settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"bindings": {
33
"transcribe": {
44
"id": "transcribe",
5-
"name": "Transcribe",
5+
"name": "Transcribe Keyboard Shortcut",
66
"description": "Converts your speech into text.",
77
"default_binding": "alt+space"
88
}

src-tauri/resources/rec_start.wav

99.9 KB
Binary file not shown.

src-tauri/resources/rec_stop.wav

136 KB
Binary file not shown.

src-tauri/src/actions.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use crate::managers::audio::AudioRecordingManager;
22
use crate::managers::transcription::TranscriptionManager;
33
use crate::utils;
44
use crate::utils::change_tray_icon;
5+
use crate::utils::play_recording_start_sound;
6+
use crate::utils::play_recording_stop_sound;
57
use crate::utils::TrayIconState;
68
use once_cell::sync::Lazy;
79
use std::collections::HashMap;
@@ -23,6 +25,9 @@ impl ShortcutAction for TranscribeAction {
2325
let binding_id = binding_id.to_string();
2426
change_tray_icon(app, TrayIconState::Recording);
2527

28+
// Play audio feedback for recording start
29+
play_recording_start_sound(app);
30+
2631
let rm = app.state::<Arc<AudioRecordingManager>>();
2732
rm.try_start_recording(&binding_id);
2833
}
@@ -34,6 +39,9 @@ impl ShortcutAction for TranscribeAction {
3439

3540
change_tray_icon(app, TrayIconState::Idle);
3641

42+
// Play audio feedback for recording stop
43+
play_recording_stop_sound(app);
44+
3745
let binding_id = binding_id.to_string(); // Clone binding_id for the async task
3846

3947
tauri::async_runtime::spawn(async move {

src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ pub fn run() {
168168
shortcut::change_binding,
169169
shortcut::reset_binding,
170170
shortcut::change_ptt_setting,
171+
shortcut::change_audio_feedback_setting,
171172
trigger_update_check
172173
])
173174
.run(tauri::generate_context!())

src-tauri/src/settings.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct ShortcutBinding {
1717
pub struct AppSettings {
1818
pub bindings: HashMap<String, ShortcutBinding>,
1919
pub push_to_talk: bool,
20+
pub audio_feedback: bool,
2021
}
2122

2223
pub const SETTINGS_STORE_PATH: &str = "settings_store.json";
@@ -47,6 +48,7 @@ pub fn get_default_settings() -> AppSettings {
4748
AppSettings {
4849
bindings,
4950
push_to_talk: true,
51+
audio_feedback: false,
5052
}
5153
}
5254

src-tauri/src/shortcut.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ pub fn change_ptt_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
103103
Ok(())
104104
}
105105

106+
#[tauri::command]
107+
pub fn change_audio_feedback_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
108+
let mut settings = settings::get_settings(&app);
109+
settings.audio_feedback = enabled;
110+
settings::write_settings(&app, settings);
111+
Ok(())
112+
}
113+
106114
fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), String> {
107115
// Parse shortcut and return error if it fails
108116
let shortcut = match binding.current_binding.parse::<Shortcut>() {

src-tauri/src/utils.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
use crate::settings;
2+
13
use enigo::Enigo;
24
use enigo::Key;
35
use enigo::Keyboard;
46
use enigo::Settings;
57

8+
use rodio::{Decoder, OutputStream, Sink};
9+
use std::fs::File;
10+
use std::io::BufReader;
11+
use std::thread;
612
use tauri::image::Image;
713
use tauri::tray::TrayIcon;
814
use tauri::AppHandle;
@@ -80,3 +86,68 @@ pub fn change_tray_icon(app: &AppHandle, icon: TrayIconState) {
8086
.expect("failed to set icon"),
8187
));
8288
}
89+
90+
/// Plays an audio resource from the resources directory.
91+
/// Checks if audio feedback is enabled in settings before playing.
92+
pub fn play_sound(app: &AppHandle, resource_path: &str) {
93+
// Check if audio feedback is enabled
94+
let settings = settings::get_settings(app);
95+
if !settings.audio_feedback {
96+
return;
97+
}
98+
99+
let app_handle = app.clone();
100+
let resource_path = resource_path.to_string();
101+
102+
// Spawn a new thread to play the audio without blocking the main thread
103+
thread::spawn(move || {
104+
// Get the path to the audio file in resources
105+
let audio_path = match app_handle
106+
.path()
107+
.resolve(&resource_path, tauri::path::BaseDirectory::Resource)
108+
{
109+
Ok(path) => path,
110+
Err(e) => {
111+
eprintln!(
112+
"Failed to resolve audio file path '{}': {}",
113+
resource_path, e
114+
);
115+
return;
116+
}
117+
};
118+
119+
// Try to play the audio file
120+
if let Err(e) = play_audio_file(&audio_path) {
121+
eprintln!("Failed to play sound '{}': {}", resource_path, e);
122+
}
123+
});
124+
}
125+
126+
/// Convenience function to play the recording start sound
127+
pub fn play_recording_start_sound(app: &AppHandle) {
128+
play_sound(app, "resources/rec_start.wav");
129+
}
130+
131+
/// Convenience function to play the recording stop sound
132+
pub fn play_recording_stop_sound(app: &AppHandle) {
133+
play_sound(app, "resources/rec_stop.wav");
134+
}
135+
136+
fn play_audio_file(path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
137+
// Get a output stream handle to the default physical sound device
138+
let (_stream, stream_handle) = OutputStream::try_default()?;
139+
140+
// Load the audio file
141+
let file = File::open(path)?;
142+
let buf_reader = BufReader::new(file);
143+
let source = Decoder::new(buf_reader)?;
144+
145+
// Create a sink to play the audio
146+
let sink = Sink::try_new(&stream_handle)?;
147+
sink.append(source);
148+
149+
// Wait for the audio to finish playing
150+
sink.sleep_until_end();
151+
152+
Ok(())
153+
}

0 commit comments

Comments
 (0)