From ac31349b32d89b2798cd20224fcdede91d6bfe05 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 30 Jun 2026 03:30:51 +0800
Subject: [PATCH] Capture Windows QA target text
---
privatevoice.src/internal/engine/engine.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/privatevoice.src/internal/engine/engine.go b/privatevoice.src/internal/engine/engine.go
index 8e2c23c..aed8e7f 100755
--- a/privatevoice.src/internal/engine/engine.go
+++ b/privatevoice.src/internal/engine/engine.go
@@ -2,9 +2,12 @@
import (
"fmt"
+ "time"
"voicesnap/internal/config"
+ "voicesnap/internal/language"
"voicesnap/internal/logger"
"voicesnap/internal/model"
+ "voicesnap/internal/modelselection"
"voicesnap/internal/paths"
)
@@ -15,6 +18,33 @@
// HardwareInfo returns a human-readable description of the hardware backend being used.
HardwareInfo() string
// Close releases engine resources.
+ Close()
+}
+
+// StreamingEngine is implemented by engines that can expose partial results
+// while audio is still being captured.
+type StreamingEngine interface {
+ NewStreamingSession() (StreamingSession, error)
+}
+
+// ReleaseTailCaptureEngine is implemented by engines that need a short audio
+// capture grace period after the user releases the recording hotkey.
+type ReleaseTailCaptureEngine interface {
+ ReleaseTailCaptureDelay() time.Duration
+}
+
+// HoldPreCaptureEngine is implemented by engines that should start capturing
+// audio immediately on hold-to-talk key down, before the activation delay has
+// confirmed that the hotkey was not part of a key combination.
+type HoldPreCaptureEngine interface {
+ HoldPreCaptureEnabled() bool
+}
+
+// StreamingSession receives incremental 16kHz mono PCM and returns the current
+// best transcript for the active utterance.
+type StreamingSession interface {
+ Accept(samples []float32) (string, error)
+ Finish() (string, error)
Close()
}
@@ -60,9 +90,10 @@
cfg, err := config.Load()
if err != nil {
logger.Error("Failed to load config for model selection: %v", err)
- return NewWithModelID(model.DefaultModelID)
+ cfg = config.Default()
}
- return NewWithModelID(cfg.SelectedModelID)
+ current := modelselection.Resolve(cfg, language.NewSystemDetector())
+ return NewWithModelID(current.ModelID)
}
func NewWithModelID(modelID string) (Engine, error) {
@@ -77,18 +108,28 @@
if !resolved.IsUsable() {
return nil, fmt.Errorf("model %s is not usable: %s missing=%v problems=%v", resolved.ModelID, resolved.Status, resolved.Missing, resolved.Problems)
}
- if resolved.BackendKind != model.BackendSenseVoice {
+ if !isSupportedBackend(resolved.BackendKind) {
return nil, fmt.Errorf("unsupported backend kind: %s", resolved.BackendKind)
}
- logger.Info("Loading model %s from %s", resolved.ModelID, resolved.RootDir)
+ logger.Info("Loading model %s backend=%s from %s", resolved.ModelID, resolved.BackendKind, resolved.RootDir)
return newPlatformEngine(resolved)
+}
+
+func isSupportedBackend(backend string) bool {
+ switch backend {
+ case model.BackendSenseVoice, model.BackendMoonshine, model.BackendTransducer, model.BackendNemoTransducer, model.BackendQwen3ASR, model.BackendXASRStreaming:
+ return true
+ default:
+ return false
+ }
}
func resolveCurrentModel() (model.ResolvedModel, error) {
cfg, err := config.Load()
if err != nil {
- return model.ResolveModel(model.DefaultModelID)
+ cfg = config.Default()
}
- return model.ResolveModel(model.NormalizeModelID(cfg.SelectedModelID))
+ current := modelselection.Resolve(cfg, language.NewSystemDetector())
+ return model.ResolveModel(current.ModelID)
}
--
Gitblit v1.9.3