agent-voice
在 macOS 上从零搭建'文本→本地 TTS→虚拟麦克风'系统并正确运行。当用户想要把文字转成语音并通过虚拟麦克风输出给其他应用(Zoom/微信/录音软件等)时使用此 skill。覆盖:MOSS-TTS-Nano ONNX 引擎部署、BlackHole 虚拟声卡安装与配置、launchd…
Skill by onesmash
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/onesmash-agent-voice
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: agent-building
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Python package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/agent-voice
What's inside
- Skill name
agent-voice- Description
- 在 macOS 上从零搭建'文本→本地 TTS→虚拟麦克风'系统并正确运行。当用户想要把文字转成语音并通过虚拟麦克风输出给其他应用(Zoom/微信/录音软件等)时使用此 skill。覆盖:MOSS-TTS-Nano ONNX 引擎部署、BlackHole 虚拟声卡安装与配置、launchd 常驻服务、品牌词发音映射、爆破音修复、闭环 CER 评分。即使用户只说'读一下这段文字'或'帮我搭个 TTS',只要涉及本地语音合成输出到虚拟麦克风,就应使用此 skill。也适用于系统已装好但朗读失败、守护进程崩溃重启、BlackHole 静默等故障排查场景。
- License
- MIT
- Instructions
- 257 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
- Instructions read files in your home folder (~/.ssh, ~/.aws…)
Agent Skills spec issues
- files: referenced but missing: scripts/selftest_report.json
Files (46)
- .gitignore
- SKILL.md
- DEPLOYMENT.md · references/
- PITCH_STABILITY_REPORT.md · references/
- __init__.py · scripts/MOSS-TTS-Nano/
- __main__.py · scripts/MOSS-TTS-Nano/
- en_2.wav · scripts/MOSS-TTS-Nano/assets/audio/
- zh_1.wav · scripts/MOSS-TTS-Nano/assets/audio/
- zh_10.wav · scripts/MOSS-TTS-Nano/assets/audio/
- zh_11.wav · scripts/MOSS-TTS-Nano/assets/audio/
- zh_3.wav · scripts/MOSS-TTS-Nano/assets/audio/
- zh_4.wav · scripts/MOSS-TTS-Nano/assets/audio/
- zh_6.wav · scripts/MOSS-TTS-Nano/assets/audio/
- cli.py · scripts/MOSS-TTS-Nano/
- defaults.py · scripts/MOSS-TTS-Nano/
- __init__.py · scripts/MOSS-TTS-Nano/moss_tts_nano/
- __main__.py · scripts/MOSS-TTS-Nano/moss_tts_nano/
- cli.py · scripts/MOSS-TTS-Nano/moss_tts_nano/
- defaults.py · scripts/MOSS-TTS-Nano/moss_tts_nano/
- moss_tts_nano_runtime.py · scripts/MOSS-TTS-Nano/
- onnx_tts_runtime.py · scripts/MOSS-TTS-Nano/
- ort_cpu_runtime.py · scripts/MOSS-TTS-Nano/
- text_normalization_pipeline.py · scripts/MOSS-TTS-Nano/
- tts_robust_normalizer_single_script.py · scripts/MOSS-TTS-Nano/
- brand_lexicon.py · scripts/
- com.moss.ttsd.plist.tmpl · scripts/
- config.toml · scripts/
- config_loader.py · scripts/
- fix_blackhole_volume.sh · scripts/
- README.txt · scripts/models/
- moss_engine.py · scripts/
- read_and_score.py · scripts/
- run.sh · scripts/
- score_cer.py · scripts/
- selftest.py · scripts/
- setup.sh · scripts/
- speak.py · scripts/
- test_sample.txt · scripts/
- text_normalization_pipeline.py · scripts/
- text_pipeline.py · scripts/
- measure_first_audio.py · scripts/tools/
- verify_render_stability.py · scripts/tools/
- verify_tts_cer.py · scripts/tools/
- tts_robust_normalizer_single_script.py · scripts/
- ttsay.py · scripts/
- ttsd.py · scripts/
SKILL.md (first lines)
# TTS 虚拟麦克风系统 — Agent 操作指南
## 系统做什么
```
用户文本 → MOSS 本地合成 → BlackHole 2ch(虚拟麦克风)→ 其他 App 收音
└→ monitor 同播(本地可听)
```
把文字转成语音,通过 BlackHole 虚拟声卡"假装"成麦克风输入。任何 App(Zoom、微信、QuickTime…)把麦克风切到 **BlackHole 2ch** 就能收到合成语音。
**播放入口只有一个:`./scripts/run.sh`**(朗读、停止都走它)。不要绕过它去直接执行 `scripts/` 里的 python 脚本——原因见[朗读文本](#朗读文本)。
## 快速部署(推荐)
```bash
./scripts/setup.sh
```
一键完成:前置检查 → venv → Python 依赖 → launchd 服务 → 等待守护进程就绪 → selftest。
两个环节会退出并要求**用户在终端人工执行**(脚本无法代输密码),按提示操作后重跑即可:
- **BlackHole 安装**:`brew install --cask blackhole-2ch` 的 pkg 安装器需要 sudo 密码,agent 直接跑必失败——不要尝试绕过,直接交给用户。
- 完成后 `sudo killall coreaudiod` 加载设备(macOS 15+ 上 `launchctl kickstart` 系统服务会被 SIP 拦截,用 `killall` 替代)。
**首次启动会自动下载 MOSS 模型 ~730MB**(走 hf-mirror 镜像,落盘到 `scripts/MOSS-TTS-Nano/models/`),属于正常流程,不要在下载中途判定为卡死。
## 前置条件(setup.sh 会逐项检查)
| 项 | 要求 | 检查命令 |
|---|---|---|
| macOS | 12+ | `sw_vers` |
| BlackHole 2ch | 已安装 | `system_profiler SPAudioDataType \| grep BlackHole` |
| Python 3.12 | venv 用 | `python3.12 --version`(缺则 `brew install python@3.12`) |
| sox + ffmpeg | 音频处理/CER 评分 | `which sox ffmpeg`(缺则 `brew install sox ffmpeg`) |
手动部署(备查,等价于 setup.sh 各步骤):
```bash
cd scripts/
python3.12 -m venv .venv-moss
# 版本 pin 按平台隔离(同 setup.sh,勿跨平台复用):x86_64 必须锁——Intel macOS 上 torch 止步
# 2.2.2,numpy 2.x 会让 torch .numpy() 报 "Numpy is not available";transformers 5.x 还要求
# torch>=2.5 会静默禁用 torch 后端。arm64 有持续更新的轮子,按原样不锁。
.venv-moss/bin/pip install "numpy<2" "onnxruntime==1.23.2" sentencepiece sounddevice soundfile \
"transformers==4.57.1" "piper-tts==1.8.0" cn2an pypinyin pypinyin-dict jieba ordered-set \
"torch==2.2.2" "torchaudio==2.2.2" torchcodec huggingface_hub # 后五个是 MOSS 引擎运行必需,不可省
# launchd:plist 由 com.moss.ttsd.plist.tmpl 模板按本机路径实例化(见 setup.sh 第 4 步)
# 守护进程只由 launchd 管理:不要手动再起一个实例(历史上会造成实例风暴)
```
## 朗读文本
```bash
./scripts/run.sh "要朗读的文本" # 朗读:阻塞至播放完成;文本可含换行,整段作为单个参数传入
./scripts/run.sh "$(cat 稿子.txt)" # 长文本/多行:用命令替换整段传入,仍是同一个入口(不要用管道)
./scripts/run.sh --stop # 停止当前播放(可从另一个终端/连接发起)
```
`run.sh` 自己不含合成逻辑,只做两件事:定位 `.venv-moss` 的 python、把参数原样转给它背后的常驻
服务客户端;客户端把文本交给 ttsd 守护进程,阻塞到播放结束,并打印首音延迟/音频时长/句数。
**为什么只留这一个入口**:脚本 shebang 是 `#!/usr/bin/env python3`,而依赖只装在 `.venv-moss` 里,
绕过 `run.sh` 直接执行脚本会因系统 python 缺依赖报 ImportError;更关键的是,"直连合成"或"手动起
服务"这类旁路都会绕开常驻服务——首音更慢、播放状态无人管理,历史上还出现过多个管理器抢 socket/
音频流的实例风暴。排障时也从 `run.sh` 出发,不要临时改走别的入口。
**守护进程不在时的预期行为**:`run.sh` 不自己拉起守护进程,只等 launchd(KeepAlive)拉起,最多等
60s;冷启动首个请求要加载+预热引擎(Apple Silicon ~5s,Intel 15–71s),期间没有输出属正常现象,
不要判定为卡死,也不要手动再起一个实例。
**停止语义**:`--stop` 置会话停止信号,两路输出(monitor 扬声器 + BlackHole 虚拟麦)在当前
0.2s 子块播完后丢弃剩余块(≤0.2s 内静音);被停止的会话回 `done(stopped=true)`,守护进程状态
干净、后续请求正常。monitor 为会话级常驻流 + 独立线程块级写(**不是** afplay 子进程——afplay
每块启停的间隙曾是"播放开头卡顿"的根因);实现全程不 abort/close 音频流(CoreAudio 状态破坏
与同设备重开挂起均已实测)。
## 配置(config.toml)
```toml
[engine]
name = "moss" # moss(质量档)| say(零安装兜底)
prompt_audio = "assets/audio/zh_6.wav" # 声音克隆参考音频(换声音=换这个文件)
number_normalize = true # 数字→中文读法(12306→一万二千三百零六)
hf_endpoint = "https://hf-mirror.com" # 模型下载镜像
[device]
name = "BlackHole 2ch"
blocksize = 256 # 实测最优(RTF 无损失)
latency = "low" # 必须显式 low;默认提示为 high
[monitor]
enabled = true # 本地同播(扬声器/耳机同步可听)
```
**用户覆盖**:`~/.config/agent-voice/config.toml` 存在时,ttsd.py 以内置 `scripts/config.toml` 为基底**逐键合并**(用户优先;dict 递归合并,标量/列表整体替换),用户配置只需写要改的键。改配置后需重启守护进程生效(`launchctl kickstart -k gui/$(id -u)/com.moss.ttsd`)。
## 架构
```
文本 → cn2an 数字归一化 → 品牌词映射 → 句级切分(≤60字)
→ MOSS AR 合成(逐句流水线)→ 有界队列(4)
→ BlackHole 输出(虚拟麦克风 → 其他 App)
→ 会话级常驻输出流(monitor → 本地扬声器/耳机)
```
## 性能基线
| 指标 | Intel MBP 实测 | Apple Silicon 实测(M4 Pro,2026-09) |
|---|---|---|
| 稳态合成 RTF | 0.85–1.05(threads=2) | 0.18–0.21 |
| 首帧延迟(热态,流式) | —(未启用) | **81–170ms,与句长无关**(整段模式 428–1470ms) |
| 引擎冷启动(加载+预热) | 15–71s | ~5s(含流式 kernel + 首连接开流/校准预热) |
| BlackHole 同流全双工回环 @256/512/1024 帧(48kHz,T5) | 26.7/32.0/64.0ms(= 1280/1536/3072 采样 = 5/3/3 个 blocksize 缓冲周期) | 同左(缓冲周期计数,**与 CPU 架构无关**) |
> **口径(T5 当前实现)**:`scripts/tools/measure_first_audio.py` 用 `sd.playrec` 在**同一 PortAudio 全双工流**内播放「0.3s 前导 + 50ms 1kHz burst」,以互相关定位 burst 到达样本,取「到达样本 − 理论样本」。该值恒为 blocksize 的整数倍(5/3/3 个回调周期),由 PortAudio/CoreAudio/BlackHole 缓冲深度决定,**不随 CPU 架构变化**—— Intel 与 Apple Silicon 数值相同不是巧合;仅当 blocksize、采样率或 BlackHole/macOS 版本变化时才会变。它不是声学/硬件时延(BlackHole 自报 out+in 缓冲为 16.0/21.3/42.7ms)。
> **历史口径(2026-09-18 首装,已废弃,勿与上表并列比较)**:旧 `measure_first_audio.py`(双标记差分版,脚本已随工作区删除、不在本仓库)给出 `abs_p50 = 205.4/216.0/226.7ms`,减去 200ms 合成延迟参数得 **5.4/16.0/26.7ms**——该三值均**小于**同表 PortAudio 自报 out+in 缓冲(16.0/21.3/42.7ms),故不可能是真实回环时延,仅为锚定在自报 `Stream.latency` 上的残差。
两代硬件绝对值差异大,但**相对行为一致**(RTF 远小于 1 即健康)。判断健康看趋势,不要硬套另一台机器的数值——**回环延迟一项两代口径不同**(新口径不线性:+5.3/+32.0ms;旧口径线性:每 +256 帧 ≈ +10.7ms),不可跨机器/跨版本直接比较。From onesmash/slm-as-harness, MIT.
More from onesmash/slm-as-harness
- 0.556score
durable-workflow-runtime
onesmash
Load when the user explicitly asks for `durable-workflow-runtime`, invokes a durable workflow shortcut, or when implementing, debugging, authoring, or…
SkillAFreshness: AClaude Code +9 - 0.529score
semif-decide
onesmash
Run runtime-defined semantic decisions ("semantic ifs") locally with the SemIf CLI (`semif-score`): give a local LLM unstructured state + a criterion question…
SkillBFreshness: BClaude Code +9 - 0.479score
context-index
onesmash
Install the Context Index system — a progressive-disclosure mechanism that intercepts large tool outputs, saves them to disk, indexes them with a local small…
SkillCFreshness: CClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Sep 23, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/onesmash/slm-as-harness
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from onesmash
- 0.556score
durable-workflow-runtime
onesmash
Load when the user explicitly asks for `durable-workflow-runtime`, invokes a durable workflow shortcut, or when implementing, debugging, authoring, or…
SkillAFreshness: AClaude Code +9 - 0.529score
semif-decide
onesmash
Run runtime-defined semantic decisions ("semantic ifs") locally with the SemIf CLI (`semif-score`): give a local LLM unstructured state + a criterion question…
SkillBFreshness: BClaude Code +9 - 0.479score
context-index
onesmash
Install the Context Index system — a progressive-disclosure mechanism that intercepts large tool outputs, saves them to disk, indexes them with a local small…
SkillCFreshness: CClaude Code +9
Cite this page
agent-voice skill. Agent Mart. https://agentmart.dev/skills/onesmash-agent-voice (last updated 2026-10-24)