mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-26 20:31:11 +08:00
139 lines
3.4 KiB
Go
139 lines
3.4 KiB
Go
package wyoming
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/AlexxIT/go2rtc/pkg/expr"
|
|
"github.com/AlexxIT/go2rtc/pkg/wav"
|
|
)
|
|
|
|
type env struct {
|
|
*satellite
|
|
Type string
|
|
Data string
|
|
}
|
|
|
|
func (s *satellite) handleEvent(evt *Event) {
|
|
switch evt.Type {
|
|
case "describe":
|
|
// {"asr": [], "tts": [], "handle": [], "intent": [], "wake": [], "satellite": {"name": "my satellite", "attribution": {"name": "", "url": ""}, "installed": true, "description": "my satellite", "version": "1.4.1", "area": null, "snd_format": null}}
|
|
data := fmt.Sprintf(`{"satellite":{"name":%q,"attribution":{"name":"go2rtc","url":"https://github.com/AlexxIT/go2rtc"},"installed":true}}`, s.srv.Name)
|
|
s.WriteEvent("info", data)
|
|
case "run-satellite":
|
|
s.Detect()
|
|
case "pause-satellite":
|
|
s.Stop()
|
|
case "detect": // WAKE_WORD_START {"names": null}
|
|
case "detection": // WAKE_WORD_END {"name": "ok_nabu_v0.1", "timestamp": 17580, "speaker": null}
|
|
case "transcribe": // STT_START {"language": "en"}
|
|
case "voice-started": // STT_VAD_START {"timestamp": 1160}
|
|
case "voice-stopped": // STT_VAD_END {"timestamp": 2470}
|
|
s.Pause()
|
|
case "transcript": // STT_END {"text": "how are you"}
|
|
case "synthesize": // TTS_START {"text": "Sorry, I couldn't understand that", "voice": {"language": "en"}}
|
|
case "audio-start": // TTS_END {"rate": 22050, "width": 2, "channels": 1, "timestamp": 0}
|
|
case "audio-chunk": // {"rate": 22050, "width": 2, "channels": 1, "timestamp": 0}
|
|
case "audio-stop": // {"timestamp": 2.880000000000002}
|
|
// run async because PlayAudio takes some time
|
|
go func() {
|
|
s.PlayAudio()
|
|
s.WriteEvent("played")
|
|
s.Detect()
|
|
}()
|
|
case "error":
|
|
s.Detect()
|
|
case "internal-run":
|
|
s.WriteEvent("run-pipeline", `{"start_stage":"wake","end_stage":"tts"}`)
|
|
s.Stream()
|
|
case "internal-detection":
|
|
s.WriteEvent("run-pipeline", `{"start_stage":"asr","end_stage":"tts"}`)
|
|
s.Stream()
|
|
}
|
|
}
|
|
|
|
func (s *satellite) handleScript(evt *Event) {
|
|
var script string
|
|
if s.srv.Event != nil {
|
|
script = s.srv.Event[evt.Type]
|
|
}
|
|
|
|
s.srv.Trace("event=%s data=%s payload size=%d", evt.Type, evt.Data, len(evt.Payload))
|
|
|
|
if script == "" {
|
|
s.handleEvent(evt)
|
|
return
|
|
}
|
|
|
|
// run async because script can have sleeps
|
|
go func() {
|
|
e := &env{satellite: s, Type: evt.Type, Data: evt.Data}
|
|
if res, err := expr.Eval(script, e); err != nil {
|
|
s.srv.Trace("event=%s expr error=%s", evt.Type, err)
|
|
s.handleEvent(evt)
|
|
} else {
|
|
s.srv.Trace("event=%s expr result=%v", evt.Type, res)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (s *satellite) Detect() bool {
|
|
return s.setMicState(stateWaitVAD)
|
|
}
|
|
|
|
func (s *satellite) Stream() bool {
|
|
return s.setMicState(stateActive)
|
|
}
|
|
|
|
func (s *satellite) Pause() bool {
|
|
return s.setMicState(stateIdle)
|
|
}
|
|
|
|
func (s *satellite) Stop() bool {
|
|
s.micStop()
|
|
return true
|
|
}
|
|
|
|
func (s *satellite) WriteEvent(args ...string) bool {
|
|
if len(args) == 0 {
|
|
return false
|
|
}
|
|
evt := &Event{Type: args[0]}
|
|
if len(args) > 1 {
|
|
evt.Data = args[1]
|
|
}
|
|
if err := s.api.WriteEvent(evt); err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *satellite) PlayAudio() bool {
|
|
return s.playAudio(sndCodec, bytes.NewReader(s.sndAudio))
|
|
}
|
|
|
|
func (s *satellite) PlayFile(path string) bool {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
codec, err := wav.ReadHeader(f)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return s.playAudio(codec, f)
|
|
}
|
|
|
|
func (e *env) Sleep(s string) bool {
|
|
d, err := time.ParseDuration(s)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
time.Sleep(d)
|
|
return true
|
|
}
|