| | |
| | | |
| | | import ( |
| | | "fmt" |
| | | "syscall" |
| | | "time" |
| | | "unsafe" |
| | | |
| | | "voicesnap/internal/logger" |
| | | "voicesnap/internal/model" |
| | | |
| | |
| | | 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 |
| | |
| | | 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" |
| | |
| | | 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() |