chenbhao commited on
Commit
de6d9e9
·
1 Parent(s): 1fc102a

feat: voice support zh-CN

Browse files

---
STT(语音识别中文)

src/hooks/useVoice.ts 两处改成:
const rawLanguage = getInitialSettings().voiceLanguage || getInitialSettings().language
优先用 voiceLanguage 字段,没有则 fallback language。zh (中文) 已在 Deepgram 支持列表中。

TTS(中文语音输出)

src/hooks/useAutoTTS.ts 新增 resolveEdgeTTSVoice() 函数,根据语言自动选择 voice:
- zh / zh-CN → zh-CN-XiaoxiaoNeural
- zh-TW → zh-TW-HsiaoYuNeural
- zh-HK → zh-HK-HiuMaanNeural
- ja → ja-JP-NanamiNeural
- 其他 → en-US-JennyNeural(默认)

配置方法(在 ~/.claude/settings.json 里加):
{
voiceLanguage: zh-CN,
voiceTTSVoice: zh-CN-XiaoxiaoNeural
}

或者更简单,改 language 为 zh-CN 也能自动生效(两处都会读)。

Files changed (3) hide show
  1. README.md +6 -1
  2. src/hooks/useAutoTTS.ts +23 -1
  3. src/hooks/useVoice.ts +3 -2
README.md CHANGED
@@ -91,7 +91,12 @@ cp VersperClaw ~/.local/bin
91
 
92
  # set auto-dream config
93
  "autoMemoryEnabled": true,
94
- "autoDreamEnabled": true
 
 
 
 
 
95
  }
96
  ```
97
 
 
91
 
92
  # set auto-dream config
93
  "autoMemoryEnabled": true,
94
+ "autoDreamEnabled": true,
95
+
96
+ # stt-tts support zh-CN
97
+ "voiceLanguage": "zh-CN",
98
+ "voiceTTSVoice": "zh-CN-XiaoxiaoNeural"
99
+
100
  }
101
  ```
102
 
src/hooks/useAutoTTS.ts CHANGED
@@ -3,6 +3,27 @@ import { speakWithEdgeTTS, playAudioFile } from '../services/voice/edgeTTS.js'
3
  import { getInitialSettings } from '../utils/settings/settings.js'
4
  import type { RenderableMessage } from '../types/message.js'
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  export function useAutoTTS(messages: RenderableMessage[]): void {
7
  const triggeredIdsRef = useRef<Set<string>>(new Set())
8
  const pendingPlayRef = useRef<Promise<void> | null>(null)
@@ -22,7 +43,8 @@ export function useAutoTTS(messages: RenderableMessage[]): void {
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, {
 
3
  import { getInitialSettings } from '../utils/settings/settings.js'
4
  import type { RenderableMessage } from '../types/message.js'
5
 
6
+ // Map language codes to Edge TTS Chinese voices
7
+ const CHINESE_VOICES: Record<string, string> = {
8
+ zh: 'zh-CN-XiaoxiaoNeural',
9
+ 'zh-CN': 'zh-CN-XiaoxiaoNeural',
10
+ 'zh-TW': 'zh-TW-HsiaoYuNeural',
11
+ 'zh-HK': 'zh-HK-HiuMaanNeural',
12
+ }
13
+
14
+ function resolveEdgeTTSVoice(language: string | undefined, explicitVoice: string | undefined): string {
15
+ if (explicitVoice) return explicitVoice
16
+ if (!language) return 'en-US-JennyNeural'
17
+ const base = language.split('-')[0].toLowerCase()
18
+ if (base === 'zh') return CHINESE_VOICES[language] || CHINESE_VOICES['zh-CN']
19
+ if (base === 'ja') return 'ja-JP-NanamiNeural'
20
+ if (base === 'ko') return 'ko-KR-SunHiNeural'
21
+ if (base === 'fr') return 'fr-FR-DeniseNeural'
22
+ if (base === 'de') return 'de-DE-KatjaNeural'
23
+ if (base === 'es') return 'es-ES-ElviraNeural'
24
+ return 'en-US-JennyNeural'
25
+ }
26
+
27
  export function useAutoTTS(messages: RenderableMessage[]): void {
28
  const triggeredIdsRef = useRef<Set<string>>(new Set())
29
  const pendingPlayRef = useRef<Promise<void> | null>(null)
 
43
  triggeredIdsRef.current.add(msg.uuid)
44
 
45
  const text = content.text
46
+ const language = settings.voiceLanguage || settings.language
47
+ const voice = resolveEdgeTTSVoice(language, settings.voiceTTSVoice)
48
 
49
  const run = async () => {
50
  const result = await speakWithEdgeTTS(text, {
src/hooks/useVoice.ts CHANGED
@@ -421,7 +421,8 @@ export function useVoice({
421
  const replayBuffer = fullAudioRef.current
422
  await sleep(250)
423
  if (isStale()) return
424
- const stt = normalizeLanguageForSTT(getInitialSettings().language)
 
425
  const keyterms = await getVoiceKeyterms()
426
  if (isStale()) return
427
  await new Promise<void>(resolve => {
@@ -764,7 +765,7 @@ export function useVoice({
764
  return
765
  }
766
 
767
- const rawLanguage = getInitialSettings().language
768
  const stt = normalizeLanguageForSTT(rawLanguage)
769
  logEvent('tengu_voice_recording_started', {
770
  focusTriggered: focusTriggeredRef.current,
 
421
  const replayBuffer = fullAudioRef.current
422
  await sleep(250)
423
  if (isStale()) return
424
+ const rawLanguage = getInitialSettings().voiceLanguage || getInitialSettings().language
425
+ const stt = normalizeLanguageForSTT(rawLanguage)
426
  const keyterms = await getVoiceKeyterms()
427
  if (isStale()) return
428
  await new Promise<void>(resolve => {
 
765
  return
766
  }
767
 
768
+ const rawLanguage = getInitialSettings().voiceLanguage || getInitialSettings().language
769
  const stt = normalizeLanguageForSTT(rawLanguage)
770
  logEvent('tengu_voice_recording_started', {
771
  focusTriggered: focusTriggeredRef.current,