From 24f551a39eae849d891da26cc91f90d354e3a2db Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 12 Jul 2026 02:44:05 +0800
Subject: [PATCH] chore(mas): refresh codebase memory after split

---
 C1.source/privatevoice.src/internal/engine/engine_windows.go |   88 +++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/C1.source/privatevoice.src/internal/engine/engine_windows.go b/C1.source/privatevoice.src/internal/engine/engine_windows.go
index 8ace9c7..6dd2ea2 100755
--- a/C1.source/privatevoice.src/internal/engine/engine_windows.go
+++ b/C1.source/privatevoice.src/internal/engine/engine_windows.go
@@ -4,7 +4,10 @@
 
 import (
 	"fmt"
+	"syscall"
 	"time"
+	"unsafe"
+
 	"voicesnap/internal/logger"
 	"voicesnap/internal/model"
 
@@ -14,10 +17,22 @@
 
 type sherpaEngine struct {
 	recognizer *sherpa.OfflineRecognizer
+	provider   string
 	hwInfo     string
 }
 
+var (
+	kernel32              = syscall.NewLazyDLL("kernel32.dll")
+	procGetShortPathNameW = kernel32.NewProc("GetShortPathNameW")
+)
+
 func newPlatformEngine(resolved model.ResolvedModel) (Engine, error) {
+	modelPath := windowsRuntimePath(resolved.Files["model"])
+	tokensPath := windowsRuntimePath(resolved.Files["tokens"])
+	if modelPath != resolved.Files["model"] || tokensPath != resolved.Files["tokens"] {
+		logger.Info("Using Windows short paths for sherpa runtime model files")
+	}
+
 	// Try DirectML first, then CPU fallback
 	providers := []struct {
 		name     string
@@ -31,10 +46,10 @@
 		config := sherpa.OfflineRecognizerConfig{}
 		config.FeatConfig.SampleRate = 16000
 		config.FeatConfig.FeatureDim = 80
-		config.ModelConfig.SenseVoice.Model = resolved.Files["model"]
+		config.ModelConfig.SenseVoice.Model = modelPath
 		config.ModelConfig.SenseVoice.Language = resolved.Profile.LanguageParam
 		config.ModelConfig.SenseVoice.UseInverseTextNormalization = 1
-		config.ModelConfig.Tokens = resolved.Files["tokens"]
+		config.ModelConfig.Tokens = tokensPath
 		config.ModelConfig.NumThreads = resolved.Profile.NumThreads
 		config.ModelConfig.Provider = p.provider
 		config.DecodingMethod = "greedy_search"
@@ -45,6 +60,7 @@
 			logger.Info("Engine initialized: %s", info)
 			return &sherpaEngine{
 				recognizer: recognizer,
+				provider:   p.provider,
 				hwInfo:     info,
 			}, nil
 		}
@@ -54,14 +70,78 @@
 	return nil, fmt.Errorf("failed to initialize sherpa-onnx with any provider")
 }
 
+func windowsRuntimePath(path string) string {
+	if path == "" {
+		return path
+	}
+	if isASCII(path) {
+		return path
+	}
+	short, ok := shortPathName(path)
+	if !ok || short == "" {
+		return path
+	}
+	return short
+}
+
+func isASCII(path string) bool {
+	for _, r := range path {
+		if r > 127 {
+			return false
+		}
+	}
+	return true
+}
+
+func shortPathName(path string) (string, bool) {
+	pathPtr, err := syscall.UTF16PtrFromString(path)
+	if err != nil {
+		return "", false
+	}
+
+	buf := make([]uint16, 32768)
+	ret, _, _ := procGetShortPathNameW.Call(
+		uintptr(unsafe.Pointer(pathPtr)),
+		uintptr(unsafe.Pointer(&buf[0])),
+		uintptr(len(buf)),
+	)
+	if ret == 0 || ret >= uintptr(len(buf)) {
+		return "", false
+	}
+
+	return syscall.UTF16ToString(buf[:int(ret)]), true
+}
+
 func (e *sherpaEngine) Recognize(samples []float32) (string, error) {
+	totalStart := time.Now()
+	streamStart := time.Now()
 	stream := sherpa.NewOfflineStream(e.recognizer)
+	streamMS := durationMS(time.Since(streamStart))
 	defer sherpa.DeleteOfflineStream(stream)
 
+	acceptStart := time.Now()
 	stream.AcceptWaveform(16000, samples)
+	acceptMS := durationMS(time.Since(acceptStart))
 
+	decodeStart := time.Now()
 	e.recognizer.Decode(stream)
+	decodeMS := durationMS(time.Since(decodeStart))
+	resultStart := time.Now()
 	result := stream.GetResult()
+	resultMS := durationMS(time.Since(resultStart))
+	logger.Info(
+		"PERF engine_recognize_detail backend=%q provider=%q engine=%q samples=%d audio_ms=%d create_stream_ms=%d accept_ms=%d decode_ms=%d result_ms=%d total_ms=%d",
+		model.BackendSenseVoice,
+		e.provider,
+		e.hwInfo,
+		len(samples),
+		audioDurationMS(samples),
+		streamMS,
+		acceptMS,
+		decodeMS,
+		resultMS,
+		durationMS(time.Since(totalStart)),
+	)
 
 	return result.Text, nil
 }
@@ -70,6 +150,10 @@
 	return e.hwInfo
 }
 
+func (e *sherpaEngine) Provider() string {
+	return e.provider
+}
+
 func (e *sherpaEngine) ReleaseTailCaptureDelay() time.Duration {
 	return senseVoiceReleaseTailDelay
 }

--
Gitblit v1.9.3