---
name: tts-zh
description: 当用户说"中文配音"、"中文 TTS"、"语音合成"、"文字转语音"、"tts-zh"，或在 workflow 里要把中文文本转成 mp3 时使用。优先用 MiniMax T2A API，失败时 fallback 到 edge-tts 离线方案，输出 mp3 到指定路径。
---

# tts-zh —— 中文 TTS（minimax 主 + edge-tts 兜底）

## 输入约定

调用方传一行 `key=value` 字段串：

```
text=今天天气真好 output=./assets/audio_0.mp3 voice=female-shaonv speed=1.0
```

字段：
- **text**（必填）：要合成的中文文本（短句，≤200 字）
- **output**（必填）：目标 mp3 路径
- **voice**（可选，默认 `female-shaonv`）：minimax 嗓音 id；edge-tts 走 `zh-CN-XiaoxiaoNeural` 不受 voice 字段影响
- **speed**（可选，默认 `1.0`）：语速 0.5–2.0

## 标准执行流程

### 1. 解析输入

仔细取 text / output / voice / speed。text 可能包含空格甚至标点，按"剩余串"策略提取。

### 2. 优先尝试 MiniMax T2A

#### 2.1 取 key + group id

```bash
KEY="$MINIMAX_API_KEY"
GROUP="$MINIMAX_GROUP_ID"
```

如果两个都不空，进入 2.2；任一为空，**直接跳到第 3 步（edge-tts fallback）**，不算错误。

#### 2.2 调 API

minimax T2A v2 接口：

```bash
jq -n --arg t "$TEXT" --arg v "$VOICE" --argjson s "$SPEED" \
  '{model:"speech-01", text:$t, voice_setting:{voice_id:$v, speed:$s}, audio_setting:{format:"mp3"}}' \
  | curl -sS "https://api.minimax.chat/v1/t2a_v2?GroupId=$GROUP" \
      -H "Authorization: Bearer $KEY" \
      -H "Content-Type: application/json" \
      --data-binary @- \
      -o /tmp/minimax_resp.json
```

> ⚠️ MINIMAX_BASE_URL 可能不是 `api.minimax.chat`（用户也许配了自定义网关）。如果 env 里有 `MINIMAX_BASE_URL`，用它替换 `https://api.minimax.chat`，否则用默认。

#### 2.3 解析响应

minimax 返回里音频是 hex 字符串：
```
.data.audio  →  "ff f3 e4 ..."（hex 编码的 mp3 字节流）
```

解码到文件：
```bash
mkdir -p "$(dirname "$OUTPUT")"
jq -r '.data.audio' /tmp/minimax_resp.json | xxd -r -p > "$OUTPUT"
```

> 如果 `xxd` 不可用（部分 Windows），用 PowerShell：
> ```powershell
> $resp = Get-Content /tmp/minimax_resp.json -Raw | ConvertFrom-Json
> $hex = $resp.data.audio
> $bytes = New-Object byte[] ($hex.Length / 2)
> for ($i = 0; $i -lt $bytes.Length; $i++) { $bytes[$i] = [Convert]::ToByte($hex.Substring($i*2, 2), 16) }
> [IO.File]::WriteAllBytes('<OUTPUT>', $bytes)
> ```

#### 2.4 校验

```bash
SIZE=$(stat -c%s "$OUTPUT" 2>/dev/null || stat -f%z "$OUTPUT")
```

- `SIZE > 1000` → 成功，输出 `saved: <OUTPUT> (provider=minimax, size=<SIZE>)`，结束
- `SIZE < 1000` 或文件不存在 → 视为失败，**进入第 3 步 edge-tts fallback**

### 3. Fallback 到 edge-tts

#### 3.1 确认 edge-tts 可用

```bash
pip show edge-tts >/dev/null 2>&1 || pip install --user edge-tts
which edge-tts || python -m edge_tts --help >/dev/null 2>&1 || true
```

如果 pip / python 都没有，**报错退出**："tts-zh 失败：minimax 不可用且本机无 Python，无法 fallback 到 edge-tts。请配置 MINIMAX_API_KEY + MINIMAX_GROUP_ID 或安装 Python + edge-tts"

#### 3.2 跑 edge-tts

```bash
edge-tts --voice zh-CN-XiaoxiaoNeural --rate "+0%" --text "$TEXT" --write-media "$OUTPUT"
# 或：
python -m edge_tts --voice zh-CN-XiaoxiaoNeural --text "$TEXT" --write-media "$OUTPUT"
```

> 语速换算：minimax 的 speed=1.0 ↔ edge-tts 的 `--rate "+0%"`；speed=1.2 ↔ `--rate "+20%"`；speed=0.8 ↔ `--rate "-20%"`。
> 公式：`rate = ((speed - 1.0) * 100)` 取整，前面加 `+` 或 `-`。

#### 3.3 校验

同 2.4 步骤；size > 1000 → 成功，输出 `saved: <OUTPUT> (provider=edge-tts, size=<SIZE>)`。

仍然失败 → 报错 "tts-zh 失败：minimax 和 edge-tts 都不工作"

## 嗓音速查

### MiniMax 常用 voice_id

| voice_id | 描述 |
|---|---|
| `female-shaonv` | 少女音（默认，自然清亮） |
| `male-qn-jingying` | 精英男声（沉稳） |
| `female-tianmei` | 甜美女声 |
| `audiobook_male_1` | 有声书男声 |

### edge-tts（兜底，固定）

| voice | 描述 |
|---|---|
| `zh-CN-XiaoxiaoNeural` | 默认女声（自然） |
| `zh-CN-YunxiNeural` | 男声 |

## 注意事项

- **不打印 API key / GroupId 到任何日志**
- text 含特殊 shell 字符（`$ "  '` 等）时务必走 jq 拼 JSON，**不要直接拼字符串到 curl 命令里**
- 输出 mp3 时长会因为文本长度而变 —— 不要硬假设时长，下游 ffprobe 实际测量
- 一次只合成一句；若调用方传超过 200 字，让它分多次调用
