Ariver
2026-06-07 605a6b2b280c0b2d555f0e2fad56b289361ce29c
privatevoice.src/internal/engine/engine_darwin.go
@@ -4,12 +4,21 @@
import (
   "fmt"
   "strings"
   "sync"
   "voicesnap/internal/logger"
   "voicesnap/internal/model"
   _ "github.com/k2-fsa/sherpa-onnx-go-macos"
   sherpa "github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx"
)
const (
   asrSampleRate          = 16000
   xasrTailPaddingSamples = asrSampleRate + asrSampleRate/2
)
var xasrTailPadding = make([]float32, xasrTailPaddingSamples)
type sherpaEngine struct {
   recognizer *sherpa.OfflineRecognizer
@@ -19,6 +28,14 @@
type xasrStreamingEngine struct {
   recognizer *sherpa.OnlineRecognizer
   hwInfo     string
   mu         sync.Mutex
}
type xasrStreamingSession struct {
   engine   *xasrStreamingEngine
   stream   *sherpa.OnlineStream
   lastText string
   finished bool
}
func newPlatformEngine(resolved model.ResolvedModel) (Engine, error) {
@@ -174,7 +191,7 @@
   stream := sherpa.NewOfflineStream(e.recognizer)
   defer sherpa.DeleteOfflineStream(stream)
   stream.AcceptWaveform(16000, samples)
   stream.AcceptWaveform(asrSampleRate, samples)
   e.recognizer.Decode(stream)
   result := stream.GetResult()
@@ -183,24 +200,97 @@
}
func (e *xasrStreamingEngine) Recognize(samples []float32) (string, error) {
   stream := sherpa.NewOnlineStream(e.recognizer)
   defer sherpa.DeleteOnlineStream(stream)
   if len(samples) > 0 {
      stream.AcceptWaveform(16000, samples)
   session, err := e.NewStreamingSession()
   if err != nil {
      return "", err
   }
   stream.InputFinished()
   defer session.Close()
   if _, err := session.Accept(samples); err != nil {
      return "", err
   }
   return session.Finish()
}
func (e *xasrStreamingEngine) NewStreamingSession() (StreamingSession, error) {
   if e.recognizer == nil {
      return nil, fmt.Errorf("X-ASR recognizer is not initialized")
   }
   stream := sherpa.NewOnlineStream(e.recognizer)
   if stream == nil {
      return nil, fmt.Errorf("failed to create X-ASR streaming session")
   }
   return &xasrStreamingSession{
      engine: e,
      stream: stream,
   }, nil
}
func (s *xasrStreamingSession) Accept(samples []float32) (string, error) {
   if len(samples) == 0 {
      return s.lastText, nil
   }
   if s.stream == nil || s.finished {
      return s.lastText, nil
   }
   s.engine.mu.Lock()
   defer s.engine.mu.Unlock()
   s.stream.AcceptWaveform(asrSampleRate, samples)
   s.lastText = s.engine.decodeReadyLocked(s.stream, s.lastText)
   return s.lastText, nil
}
func (s *xasrStreamingSession) Finish() (string, error) {
   if s.stream == nil || s.finished {
      return s.lastText, nil
   }
   s.engine.mu.Lock()
   defer s.engine.mu.Unlock()
   s.stream.AcceptWaveform(asrSampleRate, xasrTailPadding)
   s.lastText = s.engine.decodeReadyLocked(s.stream, s.lastText)
   s.stream.InputFinished()
   s.lastText = s.engine.decodeReadyLocked(s.stream, s.lastText)
   s.finished = true
   return s.lastText, nil
}
func (s *xasrStreamingSession) Close() {
   if s.stream == nil {
      return
   }
   s.engine.mu.Lock()
   defer s.engine.mu.Unlock()
   sherpa.DeleteOnlineStream(s.stream)
   s.stream = nil
}
func (e *xasrStreamingEngine) decodeReadyLocked(stream *sherpa.OnlineStream, lastText string) string {
   for e.recognizer.IsReady(stream) {
      e.recognizer.Decode(stream)
      lastText = rememberNonEmptyText(lastText, onlineResultText(e.recognizer, stream))
   }
   return rememberNonEmptyText(lastText, onlineResultText(e.recognizer, stream))
   }
   result := e.recognizer.GetResult(stream)
func onlineResultText(recognizer *sherpa.OnlineRecognizer, stream *sherpa.OnlineStream) string {
   result := recognizer.GetResult(stream)
   if result == nil {
      return "", nil
      return ""
   }
   return result.Text
   }
   return result.Text, nil
func rememberNonEmptyText(lastText, nextText string) string {
   if strings.TrimSpace(nextText) == "" {
      return lastText
   }
   return nextText
}
func (e *sherpaEngine) HardwareInfo() string {
@@ -219,6 +309,9 @@
}
func (e *xasrStreamingEngine) Close() {
   e.mu.Lock()
   defer e.mu.Unlock()
   if e.recognizer != nil {
      sherpa.DeleteOnlineRecognizer(e.recognizer)
      e.recognizer = nil