Cw
2026-07-04 fecea2c7a4ecc48b2354868894e78be361a2ccf1
Fix Windows ASR model paths for non-ASCII users
1 files modified
60 ■■■■■ changed files
C1.source/privatevoice.src/internal/engine/engine_windows.go 60 ●●●●● patch | view | raw | blame | history
C1.source/privatevoice.src/internal/engine/engine_windows.go
@@ -4,7 +4,10 @@
import (
    "fmt"
    "syscall"
    "time"
    "unsafe"
    "voicesnap/internal/logger"
    "voicesnap/internal/model"
@@ -18,7 +21,18 @@
    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
@@ -32,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"
@@ -56,6 +70,48 @@
    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()