mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-19 14:50:38 -07:00
Refactor audio capture error handling and cleanup logic
- Removed console logging from the useSystemAudioCapture hook to streamline the code. - Introduced error handling in the audio capture state to capture and report errors more effectively. - Updated the cleanup logic to ensure proper handling of errors during audio capture on unmount. - Enhanced error messages for better clarity when audio capture fails.
This commit is contained in:
@@ -41,7 +41,6 @@ export function useSystemAudioCapture({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const startRecording = useCallback(async () => {
|
const startRecording = useCallback(async () => {
|
||||||
console.log('[useSystemAudioCapture] startRecording called');
|
|
||||||
if (!isTauri()) {
|
if (!isTauri()) {
|
||||||
const errorMsg = 'System audio capture is only available in the desktop app.';
|
const errorMsg = 'System audio capture is only available in the desktop app.';
|
||||||
setError(errorMsg);
|
setError(errorMsg);
|
||||||
@@ -55,16 +54,13 @@ export function useSystemAudioCapture({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useSystemAudioCapture] Starting capture...');
|
|
||||||
setError(null);
|
setError(null);
|
||||||
setDuration(0);
|
setDuration(0);
|
||||||
|
|
||||||
// Start native capture
|
// Start native capture
|
||||||
console.log('[useSystemAudioCapture] Calling invoke...');
|
|
||||||
await invoke('start_system_audio_capture', {
|
await invoke('start_system_audio_capture', {
|
||||||
maxDurationSecs: maxDurationSeconds,
|
maxDurationSecs: maxDurationSeconds,
|
||||||
});
|
});
|
||||||
console.log('[useSystemAudioCapture] Invoke completed, starting timer');
|
|
||||||
|
|
||||||
setIsRecording(true);
|
setIsRecording(true);
|
||||||
isRecordingRef.current = true;
|
isRecordingRef.current = true;
|
||||||
@@ -78,14 +74,11 @@ export function useSystemAudioCapture({
|
|||||||
|
|
||||||
// Auto-stop at max duration
|
// Auto-stop at max duration
|
||||||
if (elapsed >= maxDurationSeconds && stopRecordingRef.current) {
|
if (elapsed >= maxDurationSeconds && stopRecordingRef.current) {
|
||||||
console.log('[useSystemAudioCapture] Max duration reached, auto-stopping');
|
|
||||||
void stopRecordingRef.current();
|
void stopRecordingRef.current();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
console.log('[useSystemAudioCapture] Timer started');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useSystemAudioCapture] Error starting recording:', err);
|
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
err instanceof Error
|
err instanceof Error
|
||||||
? err.message
|
? err.message
|
||||||
@@ -157,17 +150,15 @@ export function useSystemAudioCapture({
|
|||||||
// Cleanup on unmount only
|
// Cleanup on unmount only
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
console.log('[useSystemAudioCapture] Component unmounting, cleaning up');
|
|
||||||
if (timerRef.current !== null) {
|
if (timerRef.current !== null) {
|
||||||
clearInterval(timerRef.current);
|
clearInterval(timerRef.current);
|
||||||
timerRef.current = null;
|
timerRef.current = null;
|
||||||
}
|
}
|
||||||
// Cancel recording on unmount if still recording
|
// Cancel recording on unmount if still recording
|
||||||
if (isRecordingRef.current && isTauri()) {
|
if (isRecordingRef.current && isTauri()) {
|
||||||
console.log('[useSystemAudioCapture] Canceling recording on unmount');
|
|
||||||
// Call stop directly without the callback to avoid stale closure
|
// Call stop directly without the callback to avoid stale closure
|
||||||
invoke('stop_system_audio_capture').catch((err) => {
|
invoke('stop_system_audio_capture').catch((err) => {
|
||||||
console.error('[useSystemAudioCapture] Error stopping on unmount:', err);
|
console.error('Error stopping audio capture on unmount:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
@@ -18,6 +18,7 @@ pub struct AudioCaptureState {
|
|||||||
pub sample_rate: Arc<Mutex<u32>>,
|
pub sample_rate: Arc<Mutex<u32>>,
|
||||||
pub channels: Arc<Mutex<u16>>,
|
pub channels: Arc<Mutex<u16>>,
|
||||||
pub stop_tx: Arc<Mutex<Option<tokio::sync::mpsc::Sender<()>>>>,
|
pub stop_tx: Arc<Mutex<Option<tokio::sync::mpsc::Sender<()>>>>,
|
||||||
|
pub error: Arc<Mutex<Option<String>>>,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub stream: Arc<Mutex<Option<SCStream>>>,
|
pub stream: Arc<Mutex<Option<SCStream>>>,
|
||||||
}
|
}
|
||||||
@@ -29,6 +30,7 @@ impl AudioCaptureState {
|
|||||||
sample_rate: Arc::new(Mutex::new(44100)),
|
sample_rate: Arc::new(Mutex::new(44100)),
|
||||||
channels: Arc::new(Mutex::new(2)),
|
channels: Arc::new(Mutex::new(2)),
|
||||||
stop_tx: Arc::new(Mutex::new(None)),
|
stop_tx: Arc::new(Mutex::new(None)),
|
||||||
|
error: Arc::new(Mutex::new(None)),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
stream: Arc::new(Mutex::new(None)),
|
stream: Arc::new(Mutex::new(None)),
|
||||||
}
|
}
|
||||||
@@ -36,5 +38,6 @@ impl AudioCaptureState {
|
|||||||
|
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
*self.samples.lock().unwrap() = Vec::new();
|
*self.samples.lock().unwrap() = Vec::new();
|
||||||
|
*self.error.lock().unwrap() = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub async fn start_capture(
|
|||||||
let sample_rate_arc = state.sample_rate.clone();
|
let sample_rate_arc = state.sample_rate.clone();
|
||||||
let channels_arc = state.channels.clone();
|
let channels_arc = state.channels.clone();
|
||||||
let stop_tx = state.stop_tx.clone();
|
let stop_tx = state.stop_tx.clone();
|
||||||
|
let error_arc = state.error.clone();
|
||||||
|
|
||||||
// Use AtomicBool for stop signal (works with non-Send types)
|
// Use AtomicBool for stop signal (works with non-Send types)
|
||||||
let stop_flag = Arc::new(AtomicBool::new(false));
|
let stop_flag = Arc::new(AtomicBool::new(false));
|
||||||
@@ -42,7 +43,9 @@ pub async fn start_capture(
|
|||||||
{
|
{
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to get audio device: {}", e);
|
let error_msg = format!("Failed to get audio device: {}", e);
|
||||||
|
eprintln!("{}", error_msg);
|
||||||
|
*error_arc.lock().unwrap() = Some(error_msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -50,7 +53,9 @@ pub async fn start_capture(
|
|||||||
let mut audio_client = match device.get_iaudioclient() {
|
let mut audio_client = match device.get_iaudioclient() {
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to get audio client: {}", e);
|
let error_msg = format!("Failed to get audio client: {}", e);
|
||||||
|
eprintln!("{}", error_msg);
|
||||||
|
*error_arc.lock().unwrap() = Some(error_msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -58,7 +63,9 @@ pub async fn start_capture(
|
|||||||
let mix_format = match audio_client.get_mixformat() {
|
let mix_format = match audio_client.get_mixformat() {
|
||||||
Ok(format) => format,
|
Ok(format) => format,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to get mix format: {}", e);
|
let error_msg = format!("Failed to get mix format: {}", e);
|
||||||
|
eprintln!("{}", error_msg);
|
||||||
|
*error_arc.lock().unwrap() = Some(error_msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -76,20 +83,26 @@ pub async fn start_capture(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = audio_client.initialize_client(&mix_format, &Direction::Capture, &stream_mode) {
|
if let Err(e) = audio_client.initialize_client(&mix_format, &Direction::Capture, &stream_mode) {
|
||||||
eprintln!("Failed to initialize audio client: {}", e);
|
let error_msg = format!("Failed to initialize audio client: {}", e);
|
||||||
|
eprintln!("{}", error_msg);
|
||||||
|
*error_arc.lock().unwrap() = Some(error_msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let capture_client = match audio_client.get_audiocaptureclient() {
|
let capture_client = match audio_client.get_audiocaptureclient() {
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to get capture client: {}", e);
|
let error_msg = format!("Failed to get capture client: {}", e);
|
||||||
|
eprintln!("{}", error_msg);
|
||||||
|
*error_arc.lock().unwrap() = Some(error_msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = audio_client.start_stream() {
|
if let Err(e) = audio_client.start_stream() {
|
||||||
eprintln!("Failed to start stream: {}", e);
|
let error_msg = format!("Failed to start stream: {}", e);
|
||||||
|
eprintln!("{}", error_msg);
|
||||||
|
*error_arc.lock().unwrap() = Some(error_msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,13 +189,18 @@ pub async fn stop_capture(state: &AudioCaptureState) -> Result<String, String> {
|
|||||||
// Wait a bit for capture to stop
|
// Wait a bit for capture to stop
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||||
|
|
||||||
|
// Check if there was an error during capture
|
||||||
|
if let Some(error) = state.error.lock().unwrap().as_ref() {
|
||||||
|
return Err(error.clone());
|
||||||
|
}
|
||||||
|
|
||||||
// Get samples
|
// Get samples
|
||||||
let samples = state.samples.lock().unwrap().clone();
|
let samples = state.samples.lock().unwrap().clone();
|
||||||
let sample_rate = *state.sample_rate.lock().unwrap();
|
let sample_rate = *state.sample_rate.lock().unwrap();
|
||||||
let channels = *state.channels.lock().unwrap();
|
let channels = *state.channels.lock().unwrap();
|
||||||
|
|
||||||
if samples.is_empty() {
|
if samples.is_empty() {
|
||||||
return Err("No audio samples captured".to_string());
|
return Err("No audio samples captured. Make sure audio is playing on your system during recording.".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to WAV
|
// Convert to WAV
|
||||||
|
|||||||
Reference in New Issue
Block a user