Skip to content

Commit

Permalink
feat: add demo version page
Browse files Browse the repository at this point in the history
Signed-off-by: Xinwei Xiong <[email protected]>
  • Loading branch information
cubxxw committed Dec 17, 2024
1 parent cbd339f commit b0e6d13
Show file tree
Hide file tree
Showing 14 changed files with 864 additions and 0 deletions.
27 changes: 27 additions & 0 deletions cmd/voiceflow/realtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// cmd/voiceflow/realtime.go
package main

import (
"fmt"
"github.com/spf13/cobra"
"github.com/telepace/voiceflow/pkg/voiceprocessor"
)

var realtimeCmd = &cobra.Command{
Use: "realtime",
Short: "在终端中实时监听语音并翻译",
RunE: runRealtime,
}

func init() {
rootCmd.AddCommand(realtimeCmd)
}

func runRealtime(cmd *cobra.Command, args []string) error {
fmt.Println("启动实时语音监听...")
err := voiceprocessor.StartRealtime()
if err != nil {
return err
}
return nil
}
29 changes: 29 additions & 0 deletions cmd/voiceflow/transcribe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// cmd/voiceflow/transcribe.go
package main

//import (
// "fmt"
// "github.com/spf13/cobra"
// "github.com/telepace/voiceflow/pkg/voiceprocessor"
//)
//
//var transcribeCmd = &cobra.Command{
// Use: "transcribe [音频文件路径]",
// Short: "转录并翻译指定的音频文件",
// Args: cobra.ExactArgs(1),
// RunE: runTranscribe,
//}
//
//func init() {
// rootCmd.AddCommand(transcribeCmd)
//}
//
//func runTranscribe(cmd *cobra.Command, args []string) error {
// audioFile := args[0]
// fmt.Printf("正在转录音频文件:%s\n", audioFile)
// err := voiceprocessor.TranscribeFile(audioFile)
// if err != nil {
// return err
// }
// return nil
//}
6 changes: 6 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go 1.22.5

use (
.
./test/assemblyai
)
68 changes: 68 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk=
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s=
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nats-io/nats.go v1.34.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
github.com/sagikazarmark/crypt v0.19.0/go.mod h1:c6vimRziqqERhtSe0MhIvzE1w54FrCHtrXb5NH/ja78=
go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=
go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=
go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E=
go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
156 changes: 156 additions & 0 deletions internal/stt/aws/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// internal/stt/aws/aws.go
package aws

import (
"context"
"fmt"
"github.com/telepace/voiceflow/pkg/sttservice"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
transcribe "github.com/aws/aws-sdk-go/service/transcribestreamingservice"
"github.com/aws/aws-sdk-go/service/transcribestreamingservice/transcribestreamingserviceiface"
"github.com/telepace/voiceflow/pkg/config"
)

type Service struct {
client transcribestreamingserviceiface.TranscribeStreamingServiceAPI
config *config.AWSConfig
}

// 确保 Service 实现了 sttservice.Service 接口
var _ sttservice.Service = (*Service)(nil)

// NewService 创建新的 AWS STT 服务
func NewService(cfg *config.AWSConfig) (sttservice.Service, error) {
awsConfig := &aws.Config{
Region: aws.String(cfg.Region),
Credentials: credentials.NewStaticCredentials(cfg.AccessKeyID, cfg.SecretAccessKey, ""),
}
sess, err := session.NewSession(awsConfig)
if err != nil {
return nil, fmt.Errorf("无法创建 AWS 会话:%v", err)
}
client := transcribe.New(sess)
return &Service{
client: client,
config: cfg,
}, nil
}

// Recognize 实现了 stt.Service 接口的 Recognize 方法
func (s *Service) Recognize(audioData []byte) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()

input := &transcribe.StartStreamTranscriptionInput{
LanguageCode: aws.String("en-US"),
MediaEncoding: aws.String("pcm"),
MediaSampleRateHertz: aws.Int64(16000),
}

output, err := s.client.StartStreamTranscriptionWithContext(ctx, input)
if err != nil {
return "", fmt.Errorf("无法开始转录流:%v", err)
}

eventStream := output.GetStream()

// 发送音频数据
go func() {
defer eventStream.Close()
err := eventStream.Send(ctx, &transcribe.AudioEvent{
AudioChunk: audioData,
})
if err != nil {
fmt.Printf("发送音频数据时出错:%v\n", err)
return
}
// 发送完成后关闭发送方向的流
eventStream.Close()
}()

// 接收转录结果
var transcript string
for event := range eventStream.Events() {
switch e := event.(type) {
case *transcribe.TranscriptEvent:
results := e.Transcript.Results
for _, result := range results {
if !aws.BoolValue(result.IsPartial) {
for _, alt := range result.Alternatives {
transcript += aws.StringValue(alt.Transcript)
}
}
}
}
}

if err := eventStream.Err(); err != nil {
return "", fmt.Errorf("转录错误:%v", err)
}

return transcript, nil
}

// StreamRecognize 实现了 stt.Service 接口的 StreamRecognize 方法
func (s *Service) StreamRecognize(ctx context.Context, audioDataChan <-chan []byte, transcriptChan chan<- string) error {
input := &transcribe.StartStreamTranscriptionInput{
LanguageCode: aws.String("en-US"),
MediaEncoding: aws.String("pcm"),
MediaSampleRateHertz: aws.Int64(16000),
}

output, err := s.client.StartStreamTranscriptionWithContext(ctx, input)
if err != nil {
return fmt.Errorf("无法开始转录流:%v", err)
}

eventStream := output.GetStream()

// 发送音频数据的协程
go func() {
defer eventStream.Close()
for {
select {
case audioChunk, ok := <-audioDataChan:
if !ok {
// 音频数据通道已关闭,结束发送
return
}
err := eventStream.Send(ctx, &transcribe.AudioEvent{
AudioChunk: audioChunk,
})
if err != nil {
fmt.Printf("发送音频块时出错:%v\n", err)
return
}
case <-ctx.Done():
return
}
}
}()

// 接收转录结果
for event := range eventStream.Events() {
switch e := event.(type) {
case *transcribe.TranscriptEvent:
results := e.Transcript.Results
for _, result := range results {
for _, alt := range result.Alternatives {
transcript := aws.StringValue(alt.Transcript)
// 发送部分转录结果
transcriptChan <- transcript
}
}
}
}

if err := eventStream.Err(); err != nil {
return fmt.Errorf("转录错误:%v", err)
}

return nil
}
2 changes: 2 additions & 0 deletions internal/tts/aws/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// internal/tts/aws/aws.go
package aws
28 changes: 28 additions & 0 deletions pkg/sttservice/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// pkg/sttservice/service.go
package sttservice

import (
"context"
"fmt"
)

type Service interface {
Recognize(audioData []byte) (string, error)
StreamRecognize(ctx context.Context, audioDataChan <-chan []byte, transcriptChan chan<- string) error
}

// 需要一个全局的 STT 服务实例
var sttInstance Service

// 提供一个方法来设置 STT 服务实例
func SetService(s Service) {
sttInstance = s
}

// 提供全局可调用的 Recognize 方法
func Recognize(audioData []byte) (string, error) {
if sttInstance == nil {
return "", fmt.Errorf("STT 服务未初始化")
}
return sttInstance.Recognize(audioData)
}
39 changes: 39 additions & 0 deletions pkg/voiceprocessor/voiceprocessor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// pkg/voiceprocessor/voiceprocessor.go
package voiceprocessor

import (
"fmt"
"github.com/telepace/voiceflow/pkg/sttservice"
"os"
)

func StartRealtime() error {
// 实现实时语音监听和翻译的逻辑
fmt.Println("实时语音处理已启动。")
// 例如,使用麦克风输入并处理音频流
// 这里可以调用 sttservice 中的 StreamRecognize 方法
return nil
}

func TranscribeFile(audioFile string) error {
// 检查文件是否存在
if _, err := os.Stat(audioFile); os.IsNotExist(err) {
return fmt.Errorf("文件不存在:%s", audioFile)
}

// 读取音频文件数据
audioData, err := os.ReadFile(audioFile)
if err != nil {
return fmt.Errorf("无法读取音频文件:%v", err)
}

// 调用 STT 服务进行转录
transcript, err := sttservice.Recognize(audioData)
if err != nil {
return fmt.Errorf("转录失败:%v", err)
}

// 输出转录结果
fmt.Printf("转录结果:\n%s\n", transcript)
return nil
}
15 changes: 15 additions & 0 deletions test/assemblyai/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/telepace/voiceflow/test/assemblyai

go 1.22.5

require (
github.com/AssemblyAI/assemblyai-go-sdk v1.9.0
github.com/gordonklaus/portaudio v0.0.0-20230709114228-aafa478834f5
github.com/joho/godotenv v1.5.1
)

require (
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/google/go-querystring v1.1.0 // indirect
)
Loading

0 comments on commit b0e6d13

Please sign in to comment.