基本逻辑完成

This commit is contained in:
2024-11-01 17:40:34 +08:00
commit f9b9beea4b
40 changed files with 1869 additions and 0 deletions

80
pkg/audio/play.go Normal file
View File

@@ -0,0 +1,80 @@
package audio
import (
"context"
"github.com/gopxl/beep/v2"
"github.com/gopxl/beep/v2/mp3"
"github.com/gopxl/beep/v2/speaker"
"github.com/gopxl/beep/v2/wav"
"io"
"log"
"time"
)
var DefaultSampleRate = beep.SampleRate(44100)
func init() {
err := speaker.Init(DefaultSampleRate, DefaultSampleRate.N(time.Second/10))
if err != nil {
panic("扬声器初始化异常: " + err.Error())
}
log.Println("扬声器初始化完成")
}
func PlayWav(c context.Context, r io.Reader) {
streamer, format, err := wav.Decode(r)
if err != nil {
return
}
defer streamer.Close()
s := beep.Resample(4, format.SampleRate, DefaultSampleRate, streamer)
ctrl := &beep.Ctrl{Streamer: s}
done := make(chan struct{})
speaker.Play(beep.Seq(ctrl, beep.Callback(func() {
close(done)
})))
for {
select {
case <-done:
return
case <-c.Done():
{
speaker.Lock()
ctrl.Streamer = nil
speaker.Unlock()
}
}
}
}
func PlayMP3(c context.Context, r io.ReadCloser) {
streamer, format, err := mp3.Decode(r)
if err != nil {
return
}
defer streamer.Close()
s := beep.Resample(4, format.SampleRate, DefaultSampleRate, streamer)
ctrl := &beep.Ctrl{Streamer: s}
done := make(chan struct{})
speaker.Play(beep.Seq(ctrl, beep.Callback(func() {
close(done)
})))
for {
select {
case <-done:
return
case <-c.Done():
{
speaker.Lock()
ctrl.Streamer = nil
speaker.Unlock()
}
}
}
}

9
pkg/errorsx/error.go Normal file
View File

@@ -0,0 +1,9 @@
package errorsx
import "errors"
var DriverTimeoutErr = errors.New("处理超时")
var DriverCancelErr = errors.New("系统取消")
var ThirdPartyErr = errors.New("第三方请求异常")

7
pkg/ports.go Normal file
View File

@@ -0,0 +1,7 @@
package main
import "game-driver/pkg/relay"
func main() {
relay.PrintPorts()
}

28
pkg/relay/portlist.go Normal file
View File

@@ -0,0 +1,28 @@
package relay
import (
"fmt"
"log"
"go.bug.st/serial/enumerator"
)
func PrintPorts() {
ports, err := enumerator.GetDetailedPortsList()
if err != nil {
log.Fatal(err)
}
if len(ports) == 0 {
return
}
for _, port := range ports {
fmt.Printf("Port: %s\n", port.Name)
if port.Product != "" {
fmt.Printf(" Product Name: %s\n", port.Product)
}
if port.IsUSB {
fmt.Printf(" USB ID : %s:%s\n", port.VID, port.PID)
fmt.Printf(" USB serial : %s\n", port.SerialNumber)
}
}
}

44
pkg/relay/relay.go Normal file
View File

@@ -0,0 +1,44 @@
package relay
import (
"bufio"
"fmt"
"go.bug.st/serial"
"io"
)
type Device struct {
port serial.Port
}
func (r *Device) Close() error {
return r.port.Close()
}
func (r *Device) On(num int) error {
_, err := io.WriteString(r.port, fmt.Sprintf("AT+OUT%v+1=ON\r\n", num))
return err
}
func (r *Device) Off(num int) error {
_, err := io.WriteString(r.port, fmt.Sprintf("AT+OUT%v+1=OFF\r\n", num))
return err
}
func New(portName string, reader func(msg string)) (*Device, error) {
port, err := serial.Open(portName, &serial.Mode{
BaudRate: 9600,
DataBits: 8,
})
if err != nil {
return nil, err
}
go func() {
for {
r := bufio.NewReader(port)
line, _, _ := r.ReadLine()
reader(string(line))
}
}()
return &Device{port: port}, nil
}

101
pkg/tts/aliyun.go Normal file
View File

@@ -0,0 +1,101 @@
package tts
import (
"bytes"
"context"
"fmt"
"game-driver/config"
"game-driver/leaf"
"game-driver/pkg/audio"
"game-driver/pkg/errorsx"
"io"
"log"
"time"
nls "github.com/aliyun/alibabacloud-nls-go-sdk"
)
type AliTTS struct {
config.AliyunConfig
ctx context.Context
}
type result struct {
Data io.ReadWriter
Error error
}
// onTaskFailed 识别过程中的错误处理回调参数
func (tts *AliTTS) onTaskFailed(text string, param interface{}) {
p, _ := param.(*result)
p.Error = fmt.Errorf("语音合成异常: %v", text)
}
// onSynthesisResult 语音合成数据回调参数
func (tts *AliTTS) onSynthesisResult(data []byte, param interface{}) {
p, _ := param.(*result)
p.Data.Write(data)
}
func (tts *AliTTS) Sound(text string) {
if text == "" {
return
}
buf, err := tts.Get(text)
if err == nil && buf != nil {
audio.PlayWav(tts.ctx, buf)
} else {
log.Panicln("AliTTS 请求异常: ", err)
}
}
func (tts *AliTTS) Get(text string) (io.Reader, error) {
param := nls.DefaultSpeechSynthesisParam()
param.Volume = 100
connectConfig := nls.NewConnectionConfigWithToken(nls.DEFAULT_URL, tts.AppKey, tts.Token)
logger := nls.NewNlsLogger(leaf.DefaultWriter, "", log.LstdFlags|log.Ltime)
logger.SetLogSil(false)
logger.SetDebug(true)
ttsData := &result{
Data: &bytes.Buffer{},
}
synthesis, err := nls.NewSpeechSynthesis(
connectConfig, logger, false,
tts.onTaskFailed, tts.onSynthesisResult, nil,
nil, nil, ttsData,
)
if err != nil {
return ttsData.Data, err
}
defer synthesis.Shutdown()
ch, err := synthesis.Start(text, param, nil)
if err != nil {
return ttsData.Data, err
}
// 等待语音合成结束
select {
case done := <-ch:
{
if !done {
return ttsData.Data, errorsx.ThirdPartyErr
}
return ttsData.Data, nil
}
case <-time.After(time.Duration(tts.Timeout) * time.Second):
return ttsData.Data, errorsx.DriverTimeoutErr
case <-tts.ctx.Done():
return ttsData.Data, errorsx.DriverCancelErr
}
}
func New(ctx context.Context, config config.AliyunConfig) *AliTTS {
return &AliTTS{
ctx: ctx,
AliyunConfig: config,
}
}