chenbhao commited on
Commit
3596123
·
1 Parent(s): dca9df7

feat: voice ok

Browse files
src/hooks/useAutoTTS.ts CHANGED
@@ -5,6 +5,7 @@ import type { RenderableMessage } from '../types/message.js'
5
 
6
  export function useAutoTTS(messages: RenderableMessage[]): void {
7
  const triggeredIdsRef = useRef<Set<string>>(new Set())
 
8
 
9
  useEffect(() => {
10
  const settings = getInitialSettings()
@@ -12,18 +13,18 @@ export function useAutoTTS(messages: RenderableMessage[]): void {
12
 
13
  for (const msg of messages) {
14
  if (msg.type !== 'assistant') continue
15
- if (triggeredIdsRef.current.has(msg.id)) continue
16
 
17
  const content = msg.message.content[0]
18
  if (content?.type !== 'text') continue
19
  if (!content.text.trim()) continue
20
 
21
- triggeredIdsRef.current.add(msg.id)
22
 
23
  const text = content.text
24
  const voice = settings.voiceTTSVoice || 'en-US-JennyNeural'
25
 
26
- void (async () => {
27
  const result = await speakWithEdgeTTS(text, {
28
  voice,
29
  pythonPath: settings.voiceTTSCommand || undefined,
@@ -31,7 +32,13 @@ export function useAutoTTS(messages: RenderableMessage[]): void {
31
  if (result.success && result.audioPath) {
32
  await playAudioFile(result.audioPath)
33
  }
34
- })()
 
 
 
 
 
 
35
  }
36
  }, [messages])
37
  }
 
5
 
6
  export function useAutoTTS(messages: RenderableMessage[]): void {
7
  const triggeredIdsRef = useRef<Set<string>>(new Set())
8
+ const pendingPlayRef = useRef<Promise<void> | null>(null)
9
 
10
  useEffect(() => {
11
  const settings = getInitialSettings()
 
13
 
14
  for (const msg of messages) {
15
  if (msg.type !== 'assistant') continue
16
+ if (triggeredIdsRef.current.has(msg.uuid)) continue
17
 
18
  const content = msg.message.content[0]
19
  if (content?.type !== 'text') continue
20
  if (!content.text.trim()) continue
21
 
22
+ triggeredIdsRef.current.add(msg.uuid)
23
 
24
  const text = content.text
25
  const voice = settings.voiceTTSVoice || 'en-US-JennyNeural'
26
 
27
+ const run = async () => {
28
  const result = await speakWithEdgeTTS(text, {
29
  voice,
30
  pythonPath: settings.voiceTTSCommand || undefined,
 
32
  if (result.success && result.audioPath) {
33
  await playAudioFile(result.audioPath)
34
  }
35
+ }
36
+
37
+ if (pendingPlayRef.current) {
38
+ pendingPlayRef.current = pendingPlayRef.current.then(run, run).catch(() => {})
39
+ } else {
40
+ pendingPlayRef.current = run().catch(() => {})
41
+ }
42
  }
43
  }, [messages])
44
  }
src/services/voice/edgeTTS.ts CHANGED
@@ -140,7 +140,7 @@ export function playAudioFile(path: string): Promise<void> {
140
  args = [path]
141
  } else if (platform === 'linux') {
142
  cmd = 'ffplay'
143
- args = ['-nodisp', '-autoexit', path]
144
  } else if (platform === 'win32') {
145
  cmd = 'start'
146
  args = [path]
@@ -149,11 +149,33 @@ export function playAudioFile(path: string): Promise<void> {
149
  return
150
  }
151
 
152
- const proc = spawn(cmd, args, { stdio: 'ignore' })
153
- proc.on('close', code => {
154
- if (code === 0) resolve()
155
- else reject(new Error(`Playback exited with code ${code}`))
156
- })
157
- proc.on('error', reject)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  })
159
  }
 
140
  args = [path]
141
  } else if (platform === 'linux') {
142
  cmd = 'ffplay'
143
+ args = ['-nodisp', '-autoexit', '-infbuf', path]
144
  } else if (platform === 'win32') {
145
  cmd = 'start'
146
  args = [path]
 
149
  return
150
  }
151
 
152
+ if (platform === 'linux') {
153
+ const killProc = spawn('pkill', ['-9', 'ffplay'], { stdio: 'ignore' })
154
+ killProc.on('close', () => {
155
+ setTimeout(() => {
156
+ const proc = spawn(cmd, args, { stdio: 'ignore' })
157
+ proc.on('close', code => {
158
+ if (code === 0) resolve()
159
+ else resolve()
160
+ })
161
+ proc.on('error', () => resolve())
162
+ }, 50)
163
+ })
164
+ killProc.on('error', () => {
165
+ const proc = spawn(cmd, args, { stdio: 'ignore' })
166
+ proc.on('close', code => {
167
+ if (code === 0) resolve()
168
+ else resolve()
169
+ })
170
+ proc.on('error', () => resolve())
171
+ })
172
+ } else {
173
+ const proc = spawn(cmd, args, { stdio: 'ignore' })
174
+ proc.on('close', code => {
175
+ if (code === 0) resolve()
176
+ else reject(new Error(`Playback exited with code ${code}`))
177
+ })
178
+ proc.on('error', reject)
179
+ }
180
  })
181
  }