@xdfnet/ispeak 1.6.12 → 1.6.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/main.go +33 -63
  2. package/package.json +1 -1
package/main.go CHANGED
@@ -47,81 +47,51 @@ type StreamPlayer interface {
47
47
  Abort() error
48
48
  }
49
49
 
50
- // 单播放器:新的打断旧的,不用队列
50
+ // 最简单的播放器:channel 队列,串行播报
51
51
  type Player struct {
52
- mu sync.Mutex
53
- gen int64
54
- currentGen int64
55
- player StreamPlayer
52
+ ch chan job
53
+ }
54
+
55
+ type job struct {
56
+ text string
57
+ voice VoiceInfo
58
+ cfg Config
56
59
  }
57
60
 
58
61
  func NewPlayer() *Player {
59
- return &Player{}
62
+ p := &Player{ch: make(chan job, 256)}
63
+ go p.loop()
64
+ return p
60
65
  }
61
66
 
62
67
  func (p *Player) Submit(text string, voice VoiceInfo, cfg Config) {
63
- p.mu.Lock()
64
- p.gen++
65
- currentGen := p.gen
66
- p.player = nil
67
-
68
- player, err := newDefaultStreamPlayer()
69
- if err != nil {
70
- log.Printf("启动播放器失败: %v", err)
71
- p.mu.Unlock()
72
- return
73
- }
74
- p.player = player
75
68
  log.Printf("TTS: %s", text)
69
+ p.ch <- job{text, voice, cfg}
70
+ }
76
71
 
77
- startedAt := time.Now()
78
- go func() {
79
- defer func() {
80
- if r := recover(); r != nil {
81
- log.Printf("播报崩溃: %v", r)
82
- }
83
- }()
84
-
85
- onAudio := func(audio []byte) error {
86
- p.mu.Lock()
87
- stale := currentGen != p.gen
88
- p.mu.Unlock()
89
- if stale {
90
- return errors.New("stale")
91
- }
92
- if err := player.Write(audio); err != nil {
93
- return err
94
- }
95
- if len(audio) > 0 {
96
- log.Printf("首个音频 chunk elapsed=%s bytes=%d", time.Since(startedAt).Round(time.Millisecond), len(audio))
97
- }
98
- return nil
72
+ func (p *Player) loop() {
73
+ for j := range p.ch {
74
+ player, err := newDefaultStreamPlayer()
75
+ if err != nil {
76
+ log.Printf("启动播放器失败: %v", err)
77
+ continue
99
78
  }
79
+ p.play(j, player)
80
+ _ = player.CloseAndWait()
81
+ }
82
+ }
100
83
 
101
- if err := synthesizeStream(context.Background(), cfg, text, &voice, onAudio); err != nil {
102
- if !strings.Contains(err.Error(), "stale") {
103
- log.Printf("TTS 合成失败: %v", err)
104
- }
105
- _ = player.Abort()
106
- p.mu.Lock()
107
- if p.player == player {
108
- p.player = nil
109
- }
110
- p.mu.Unlock()
111
- return
112
- }
84
+ func (p *Player) play(j job, player StreamPlayer) {
85
+ startedAt := time.Now()
86
+ onAudio := func(audio []byte) error {
87
+ return player.Write(audio)
88
+ }
113
89
 
114
- log.Printf("TTS 流结束 elapsed=%s", time.Since(startedAt).Round(time.Millisecond))
115
- if err := player.CloseAndWait(); err != nil {
116
- log.Printf("播放器失败: %v", err)
117
- }
118
- p.mu.Lock()
119
- if p.player == player {
120
- p.player = nil
121
- }
122
- p.mu.Unlock()
123
- }()
124
- p.mu.Unlock()
90
+ if err := synthesizeStream(context.Background(), j.cfg, j.text, &j.voice, onAudio); err != nil {
91
+ log.Printf("TTS 合成失败: %v", err)
92
+ return
93
+ }
94
+ log.Printf("TTS: 完成 elapsed=%s", time.Since(startedAt).Round(time.Millisecond))
125
95
  }
126
96
 
127
97
  // 音色信息
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdfnet/ispeak",
3
- "version": "1.6.12",
3
+ "version": "1.6.13",
4
4
  "description": "Local macOS TTS daemon for AI coding assistants, powered by Volcengine streaming TTS.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/xdfnet/iSpeak#readme",