fix: 修复语音停顿导致提前粘贴的问题(累积文本,松开按钮才粘贴)

This commit is contained in:
2026-03-01 06:34:55 +08:00
parent 350e405fac
commit bfaa792760
2 changed files with 42 additions and 11 deletions

View File

@@ -74,7 +74,7 @@ func Dial(cfg Config, resultCh chan<- wsMsg.ServerMsg) (*Client, error) {
EnableDDC: true, EnableDDC: true,
ShowUtterances: false, ShowUtterances: false,
ResultType: "single", ResultType: "single",
EndWindowSize: 400, EndWindowSize: 2000,
}, },
} }
data, err := EncodeFullClientRequest(req) data, err := EncodeFullClientRequest(req)
@@ -132,10 +132,15 @@ func (c *Client) readLoop(resultCh chan<- wsMsg.ServerMsg) {
resultCh <- wsMsg.ServerMsg{Type: wsMsg.MsgError, Message: resp.ErrMsg} resultCh <- wsMsg.ServerMsg{Type: wsMsg.MsgError, Message: resp.ErrMsg}
return return
} }
// nostream mode: result comes after last audio packet or >15s // nostream mode: may return intermediate results every ~15s
text := resp.Text text := resp.Text
if text != "" { if text != "" {
resultCh <- wsMsg.ServerMsg{Type: wsMsg.MsgFinal, Text: text} if resp.IsLast {
resultCh <- wsMsg.ServerMsg{Type: wsMsg.MsgFinal, Text: text}
} else {
// Intermediate result (>15s audio) — preview only, don't paste
resultCh <- wsMsg.ServerMsg{Type: wsMsg.MsgPartial, Text: text}
}
} }
if resp.IsLast { if resp.IsLast {
return return

View File

@@ -62,23 +62,32 @@ func (h *Handler) handleConn(c *websocket.Conn) {
defer close(resultCh) defer close(resultCh)
// Writer goroutine: single writer to avoid concurrent writes // Writer goroutine: single writer to avoid concurrent writes
// Accumulates all result texts; paste is triggered by stop, not by ASR final.
var wg sync.WaitGroup var wg sync.WaitGroup
var accMu sync.Mutex
var accText string
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
for msg := range resultCh { for msg := range resultCh {
// Accumulate text from both partial and final results
if msg.Type == MsgPartial || msg.Type == MsgFinal {
accMu.Lock()
accText += msg.Text
// Send accumulated preview to phone
preview := ServerMsg{Type: MsgPartial, Text: accText}
accMu.Unlock()
if err := c.WriteMessage(websocket.TextMessage, preview.Bytes()); err != nil {
log.Warn("ws write error", "err", err)
return
}
continue
}
// Forward other messages (error, pasted) as-is
if err := c.WriteMessage(websocket.TextMessage, msg.Bytes()); err != nil { if err := c.WriteMessage(websocket.TextMessage, msg.Bytes()); err != nil {
log.Warn("ws write error", "err", err) log.Warn("ws write error", "err", err)
return return
} }
// Auto-paste on final result
if msg.Type == MsgFinal && msg.Text != "" && h.pasteFunc != nil {
if err := h.pasteFunc(msg.Text); err != nil {
log.Error("auto-paste failed", "err", err)
} else {
_ = c.WriteMessage(websocket.TextMessage, ServerMsg{Type: MsgPasted}.Bytes())
}
}
} }
}() }()
@@ -119,6 +128,10 @@ func (h *Handler) handleConn(c *websocket.Conn) {
if active { if active {
continue continue
} }
// Reset accumulated text for new session
accMu.Lock()
accText = ""
accMu.Unlock()
sa, cl, err := h.asrFactory(resultCh) sa, cl, err := h.asrFactory(resultCh)
if err != nil { if err != nil {
log.Error("asr start failed", "err", err) log.Error("asr start failed", "err", err)
@@ -134,12 +147,25 @@ func (h *Handler) handleConn(c *websocket.Conn) {
if !active { if !active {
continue continue
} }
// Finish ASR session — waits for final result from readLoop
if cleanup != nil { if cleanup != nil {
cleanup() cleanup()
cleanup = nil cleanup = nil
} }
sendAudio = nil sendAudio = nil
active = false active = false
// Now paste the accumulated text
accMu.Lock()
finalText := accText
accText = ""
accMu.Unlock()
if finalText != "" && h.pasteFunc != nil {
if err := h.pasteFunc(finalText); err != nil {
log.Error("auto-paste failed", "err", err)
} else {
resultCh <- ServerMsg{Type: MsgPasted}
}
}
log.Info("recording stopped") log.Info("recording stopped")
case MsgPaste: case MsgPaste: