From 14f801be6ea478678209c381fd188d7a58eadf93 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Mon, 18 May 2026 01:29:40 +0800
Subject: [PATCH] fix: avoid recorder stop deadlock

---
 VoiceSnapGo/internal/audio/recorder.go |   59 +++++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/VoiceSnapGo/internal/audio/recorder.go b/VoiceSnapGo/internal/audio/recorder.go
index 94bdd10..43dc995 100755
--- a/VoiceSnapGo/internal/audio/recorder.go
+++ b/VoiceSnapGo/internal/audio/recorder.go
@@ -9,9 +9,9 @@
 )
 
 const (
-	sampleRate     = 16000
-	channels       = 1
-	bitsPerSample  = 16
+	sampleRate       = 16000
+	channels         = 1
+	bitsPerSample    = 16
 	silenceThreshold = 0.05
 )
 
@@ -25,8 +25,8 @@
 type Recorder struct {
 	mu sync.Mutex
 
-	ctx     *malgo.AllocatedContext
-	device  *malgo.Device
+	ctx    *malgo.AllocatedContext
+	device *malgo.Device
 
 	// PCM buffer (16-bit signed, little-endian)
 	pcmBuf []byte
@@ -35,11 +35,11 @@
 	preferredDevice string
 
 	// State
-	isRecording      bool
-	maxVolume        float64
-	currentVolume    float64
-	volumeCallback   func(float64)
-	deviceChangeCb   func(string)
+	isRecording    bool
+	maxVolume      float64
+	currentVolume  float64
+	volumeCallback func(float64)
+	deviceChangeCb func(string)
 }
 
 // NewRecorder creates a new audio recorder backed by malgo.
@@ -151,19 +151,21 @@
 
 // Stop stops recording and discards audio data.
 func (r *Recorder) Stop() {
+	device := r.detachDevice()
+	stopAndUninitDevice(device)
+
 	r.mu.Lock()
 	defer r.mu.Unlock()
-
-	r.stopDevice()
 	r.pcmBuf = nil
 }
 
 // StopAndGetSamples stops recording and returns the captured audio as float32 samples.
 func (r *Recorder) StopAndGetSamples() []float32 {
+	device := r.detachDevice()
+	stopAndUninitDevice(device)
+
 	r.mu.Lock()
 	defer r.mu.Unlock()
-
-	r.stopDevice()
 
 	if len(r.pcmBuf) < 2 {
 		return nil
@@ -203,23 +205,36 @@
 
 // Close releases all audio resources.
 func (r *Recorder) Close() {
+	device := r.detachDevice()
+	stopAndUninitDevice(device)
+
 	r.mu.Lock()
 	defer r.mu.Unlock()
-
-	r.stopDevice()
 	if r.ctx != nil {
 		r.ctx.Free()
 		r.ctx = nil
 	}
 }
 
-func (r *Recorder) stopDevice() {
-	if r.device != nil {
-		r.device.Stop()
-		r.device.Uninit()
-		r.device = nil
-	}
+func (r *Recorder) detachDevice() *malgo.Device {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	device := r.device
+	r.device = nil
 	r.isRecording = false
+	return device
+}
+
+func stopAndUninitDevice(device *malgo.Device) {
+	if device == nil {
+		return
+	}
+	logger.Info("Stopping audio device")
+	device.Stop()
+	logger.Info("Audio device stopped")
+	device.Uninit()
+	logger.Info("Audio device uninitialized")
 }
 
 func (r *Recorder) onData(input []byte) {

--
Gitblit v1.9.3