CommunityCoding & Developmentgithub.com

chinese-tts

Text normalization (TN) and speech synthesis engineering for making programs read Chinese text aloud (with English, numbers, links, and code mixed in). Covers: converting text to pronunciation, sentence segmentation, handling streaming boundaries for numbers/years/phone numbers/verification codes/units/temperatures/percentages/ranges/fractions/internet slang, pronunciation replacement tables, debugging mispronunciations, local TTS engine selection (Kokoro, Matcha, CosyVoice, Qwen3-TTS, sherpa-onnx, vLLM, WSL2), first-chunk latency, cancellation, caching, fallback audio sources, VRAM usage, voice input and barge-in. Includes 139 verified original-to-pronunciation test cases and a validation script.

What is chinese-tts?

This skill provides a comprehensive solution for Chinese text-to-speech (TTS) text normalization and engineering, specifically designed for AI agents like Claude Code, Cursor, and Codex that need to read Chinese text aloud with correct pronunciation of numbers, years, phone numbers, units, percentages, ranges, fractions, and internet slang. It handles mixed-language content (Chinese with English, numbers, links, and code) and addresses common complaints such as "numbers read wrong," "year read as two thousand and twenty-six instead of twenty-twenty-six," "phone number read as hundreds of millions," "links read character by character," "English skipped," "streaming output differs from full sentence," and "next sentence slow after cancellation." The skill defines a clear workflow: first convert text to pronunciation, then split into sentences, always maintaining streaming boundaries by waiting for context before releasing digits or symbols. It provides 139 verified test cases in `references/golden-cases.json` and a validation script `scripts/check-tn.mjs` to test any implementation. It also covers engineering best practices for local TTS engines including Kokoro, Matcha, CosyVoice, Qwen3-TTS, sherpa-onnx, and vLLM, focusing on first-chunk latency (as low as 0.65s with Matcha), true cancellation to avoid 4.9s delays, caching (2.6–3s to 6ms), fallback audio sources, and VRAM management. The skill is ideal for developers building reading assistants, voice assistants, AI character voices, audiobooks, announcements, or any TTS preprocessing where Chinese text normalization is critical.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RedCarpG/redcarpg-skills/tree/HEAD/skills/chinese-tts

Installed? Explore more Coding & Development skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Chinese Text-to-Speech: Text Normalization and Engineering

This skill comes from an AI desktop pet that talks all day (see "Source & Maintenance" at the end): Claude streams responses, sentence by sentence, to a local TTS engine to read aloud. All rules below are tested, and numbers are measured.

One-sentence principle: Convert the pronunciation on your side; the engine just speaks it. If you leave it to the engine, changing the engine changes the pronunciation; even switching to an off-the-shelf TN library causes several regressions. Use off-the-shelf libraries (wetext, PaddleSpeech) as references and to fill gaps.

Workflow

Model stream text
  → ① Filter: remove decorations, emoji, list markers; replace code blocks, tables, links with a one-sentence summary (must recognize split tokens in streaming)
  → ② Convert to pronunciation: numbers, times, units, symbols, pronunciation replacement table (hold boundaries in streaming)
  → ③ Split sentences: first segment at the first comma, then split at commas when exceeding 40 characters
  → ④ Synthesize: only generate ahead by about 20 seconds; truly stop inference on interruption

Order is fixed: convert first, then split. Splitting first then cleaning caused three errors in practice:

OriginalResult after split-firstProblem
下午14:30开会下午14: / 30开会Colon treated as comma; time split
看这里:https://…/pull/7 就行https: / //…/pull/7 就行Link split in half; address read character by character
大概3~5天大概35天~ removed as decoration; read as "thirty-five days"

The half-width : is only treated as a comma when followed by whitespace.

Streaming Must Hold Boundaries

Correct for full sentences doesn't mean correct for streaming. When character-by-character streaming, "工作了26年" and "80年代" are read as "two-six years" and "eight-zero generation" because the following characters haven't arrived yet; the -10°C part was read as English.

  • Wait 2 more characters after a digit before releasing; recognize units even with a space in between (hold "跑了 5 " and wait for "km").
  • When moving to the next segment, carry over the last few characters (to know "了 26 年" you need the preceding "了").
  • Hold at most 200 characters; for code blocks and tables, hold at most 4000; release when limit is reached.
  • Each test must run both character-by-character and as a full sentence; results must match.

Quick Rule Reference (see references/rules.md for details)

CategoryExamplePronunciation
Year2026年, 2026届, 26年见Digit by digit: èr líng èr liù nián, èr líng èr liù jiè, èr liù nián jiàn
Count of years工作了26年, 80年代, 26年前Numeric: èr shí liù nián, bā shí nián dài, èr shí liù nián qián
NumbersPhone, verification code, 编号27149, 房间1201Digit by digit, 1 read as "yāo", pause at hyphens and spaces
Five-digit number95588 / 价格 12999No quantifier, not ending in 0, not preceded by money → number; otherwise numeric
Internet slang666, 2333, 520 alone / 666个赞, 花了888Digit by digit / numeric (only recognized from a fixed list)
2 before unit2个, 2L, 2℃liǎng gè, liǎng shēng; exception for temperature: èr shè shì dù
Units60km/h, 3kWh, 5min, 10mgměi xiǎo shí liù shí gōng lǐ, sān dù diàn, wǔ fēn zhōng, shí háo kè
Units not to convert4g网络, 5A景区, 16GBPass through as-is to the engine
Ranges, Percentages3~5%, 3-5km, -1.5~2bǎi fēn zhī sān dào wǔ, sān dào wǔ gōng lǐ, fù yī diǎn wǔ dào èr
Time14:30, 13:00, 9:05shí sì diǎn bàn, shí sān diǎn, jiǔ diǎn líng wǔ (24-hour format)
Date, Fraction10/26 交 / 有7/12的观众shí yuè èr shí liù hào / shí èr fēn zhī qī (only fraction when preceded by "有, 占, 约")
Symbols#15, 2x, x3, 100wshí wǔ hào, liǎng bèi, chéng sān, yī bǎi wàn

Validate with Test Cases

references/golden-cases.json contains 115 full-sentence cases and 24 streaming cases, all verified against the original project. scripts/check-tn.mjs tests your implementation:

node scripts/check-tn.mjs ./my-tn.mjs                  # Module must export normalize(text) and (optional) createStream()
node scripts/check-tn.mjs ./my-tn.mjs --only 号码      # Test only one group
  • Comparisons ignore whitespace and common punctuation; only compare pronunciation. Cases with note are "reference" — reported but not counted as failure.
  • Use these as acceptance criteria when writing new TN; run them after any rule change to avoid regressions.
  • Controversial pronunciations (e.g., "this computer 12999" being read as a number) are accepted by the original project. If your product wants different behavior, update the test cases and justify why.

Debugging Mispronunciations

There are four categories: text not converted to pronunciation before synthesis; polyphonic characters guessed wrong by the model; reference audio issues; random (just regenerate).

Use the current voice and two other voices to read the same sentence: if all voices mispronounce the same character, add a pronunciation replacement entry; if only one voice sounds strange, switch voices. Log both original and converted text (jsonl) every time for later analysis.

Engineering (see references/engines.md)

  • First-chunk latency: First segment only up to the first comma. With Matcha, first audio arrives 0.65s after the first character.
  • Most effective speedup is caching: CosyVoice first block 2.6–3s for uncached sentences; 6ms when cached. Pre-synthesize filler phrases ("um", "let me think") when user starts speaking.
  • Cancellation must truly stop inference: Stop vLLM by abort_request on connection drop, otherwise next sentence waits 4.9s (0.8–1.7s after implementing).
  • English may be silently skipped: Some engines don't pronounce GitHub, PR, %; re-validate English before upgrading dependencies.
  • Fallback audio source: If primary source is not ready or fails on first utterance, switch to fallback; don't retry if error occurs mid-sentence. HTTP service being up doesn't mean model is loaded; retry on 503.

Source & Maintenance

  • Source: Cirra (AI desktop pet, RedCarpG/cirra-deskpet) and its voice service cirra-voice, 2026-10 to 2026-11.
    • Code: sidecar/src/tts/speech/ (numbers.ts, units.ts, links.ts, filter.ts, overrides.ts, speech.test.ts), sidecar/src/tts/splitter.ts, sidecar/src/tts/pronunciations.ts.
    • Documentation: docs/speech.md, docs/speech-tn.md (119 three-way comparisons), docs/tts.md, docs/voices.md, docs/voice-chat.md, docs/voice-chat-2.md, docs/voice-install.md, docs/perf-checkup.md.
    • Lessons: Private repo lessons/chinese-tts.md.
  • Origin: skills/chinese-tts/ in RedCarpG/redcarpg-skills. The copy in ~/.claude/skills/ is an installed copy; do not modify directly. references/golden-cases.json was extracted from Cirra's speech.test.ts on 2026-11-10 via script; the two are not automatically synced.
  • How to modify:
    • Modify in redcarpg-skills via PR; increment version in manifest.json, validate with node scripts/validate.mjs, release with tag chinese-tts-v<version>, then reinstall local copy. Check lessons/chinese-tts.md for new items to incorporate.
    • When Cirra's TN rules change, re-extract test cases (extract [original, pronunciation] pairs from readNumbers and "streaming" groups in speech.test.ts) and use an adapter module with Cirra's compiled sidecar/dist/tts/ to run check-tn.mjs; all should pass.
    • Engine metrics (latency, real-time factor, VRAM) were measured on a machine with 8 GB VRAM and CPU lacking VNNI in 2026-10; re-measure on different hardware.
  • Changelog:
    • 2026-11-12 · 1.0.0, moved to redcarpg-skills for public release. Removed local absolute paths; source project written as links; content unchanged.
    • 2026-11-10 · Cirra · Initial version: workflow & order, streaming boundaries, quick rules and details, mispronunciation debugging, engine engineering; 139 test cases and check-tn.mjs (passed with Cirra's implementation); two test prompts in evals/.

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

frontend-verification

改完网页/前端界面(CSS、布局、组件、动画、响应式、深色模式、打印)之后怎样验证并拿出证据:先用内置浏览器预览,覆盖不到时用无头 Chrome 截图或读 DOM(预览面板被隐藏、批量截多个视口、要先进入某个状态、要拿 DOM 结果),附可直接运行的 shot.sh / shot.mjs 脚本。还收录无头 Chrome 的坑(requestAnimationFrame 动画停在第一帧、375 宽被撑成 500、深色模式跟随系统)、字体不显示或像宋体的排查、整页截图里滚动动画区块空白、vitest 排除 .claude worktree,以及桌面/手机/英文/繁體/深浅色/打印的核对清单。只要改了界面、要截图、要查响应式或深色模式、动画不动,或者准备说“界面改好了”,就用这个 skill,哪怕用户只说“看看效果”。Use after any UI/CSS/layout/animation change to verify it, take screenshots at several viewports, check dark mode / mobile / print, or debug animations frozen in headless Chrome.

immersive-cultural-ui

做重氛围、带东方古典或神秘学气质的沉浸式网页:深色夜空、细金线、薄雾、宣纸纹理、竖排中文、书法批注、景深视差、开场时逐层亮起。适用于命理排盘(八字、紫微、占星、塔罗)、黄历与二十四节气、星图、典籍诗词、博物馆展品、节庆和有仪式感的产品页。只要用户想要「有氛围、沉浸、古风、国风、东方、神秘、仪式感」的页面,或者要竖排、书法字体、星空、水墨、宣纸效果,即使没有点名风格,也用这个 skill。它提供一套带具体数值的风格配方(天书),装饰、动效、竖排和字体的做法,可复用代码和交付前检查清单。和 web-design-engineer 配合:那边管通用流程,这里管这一种风格。Also use for English requests for immersive, mystical, Chinese-classical, almanac, astrology, tarot or museum-exhibit pages.

RedCarpG/animation-capture

把会动的东西按固定时间一帧一帧录下来,做成左右对比的 GIF 或逐帧对照图,给人验收、对比改动前后、放进说明或 PR:网页的 CSS 过渡和动画、element.animate()、requestAnimationFrame 写的 JS 动画、canvas 游戏、three.js / VRM 角色、PixiJS / Live2D 角色都适用。附脚本:record.mjs(无头 Chrome + 假时钟,页面不用改,渲染多慢时间轴都准,两次录的逐帧一样)、make_gif.py(几个变体并排、标签、共用调色板)、clock-shim.js。还讲先逐帧记数值再录 GIF、开关前后怎么做变体、怎么取景,以及实测过的坑(Chrome 虚拟时间不可靠、等加载时别推时钟、Vite 首次打开会刷新页面、Live2D 运行库要先加载、无头 Chrome 用完要关、别人模型的许可)。只要用户想「录个 GIF」「做个动图给我看」「对比一下改之前和改之后的动画」「把动作录下来」「验收一下动画效果」,或者要给动画、动效、角色动作拿出能看的证据,就用这个 skill,即使没提 GIF。Also use for English requests to record an animation as frames or a GIF, make a before/after comparison of motion, or capture deterministic frames from a web page, canvas, three.js, VRM or Live2D scene.

RedCarpG/live2d-vrm-animation

在网页或桌面应用里让 Live2D(Cubism,PixiJS)和 VRM(three.js + @pixiv/three-vrm)角色动起来:加载顺序、程序姿势和动作片段互相覆盖、在物理之前叠加、VTube Studio 模型补齐(没有 Idle、没眨眼、头角度是物理输出、8K 贴图、包围盒被特效带偏)、动作通道和定住、表情存取和道具冲突、three.js AnimationMixer 的坑(不写没变的值、LoopOnce 提前关掉)、程序「带动层」让手 K 和 AI 动作不僵、动捕片段只取上半身、弹簧骨骼延迟和跟节拍、裙子穿模、让 AI 生成表情和动作的数据格式与校验规则,以及逐帧记录的验证方法。只要用户在做虚拟形象、桌宠、VTuber 工具、数字人,提到 Live2D、Cubism、model3.json、motion3、exp3、physics3、VTube Studio、VRM、VRMA、three-vrm、Mixamo、动捕、SpringBone,或者抱怨「动作一顿一顿的」「动作僵」「手放下又抬起来闪一下」「头不跟着动」「定不住」「表情看不出来」「做动作脸上没表情」「手指僵、直板手」「托腮够不到下巴」「眼睛呆、一直盯着」「腿站不稳、脚滑」,就用这个 skill。Also use for English requests about animating Live2D or VRM avatars, procedural idle motion, physics, expressions, or AI-generated character motions.

vrm-model-conversion

把带骨架的 3D 人物模型(FBX、PMX/MMD、XPS、glTF/GLB、.blend;来自 Mixamo、MMD、Unity/VRChat、Daz Genesis、Auto-Rig Pro、Rigify、3ds Max Biped、游戏提取)在 Blender 后台转成 VRM 1.0,给 three-vrm 网页、桌宠、VTuber 软件、VRChat 外的应用用。管:解压和挑哪份源文件、各格式怎么导入、骨头名自动对应到 VRM 标准骨骼、A-pose 转 T-pose、表情(已有形态键直接绑;只有脸部骨头的烘成形态键)、头发和衣服的弹簧骨骼(没有物理骨的就加骨链、按位置重新分权重)、碰撞体、MToon 材质和发片透明、贴图缩小、VRM meta 和许可,以及一份检查清单。附可复用的 Blender 脚本 vrm_kit.py(参数化,不写死文件名)、模板 build_template.py、不用 Blender 的检查脚本 inspect_vrm.py。只要用户想「把这个模型转成 VRM」「这个 FBX/PMX 能不能给 three-vrm 用」「给模型加表情、加头发物理、加弹簧骨」「VRM 的表情不动、头发不飘、裙子穿模、碰撞体没用」「下载了个模型想当桌宠/虚拟形象」,就用这个 skill,即使没说 VRM。Also use for English requests to convert FBX/PMX/glTF/blend avatars to VRM, set up VRM expressions, spring bones or colliders, or debug a VRM export from Blender.

Related Skills