# TTS Server 接口规范与数据协议

## 概述

TTS Server 是基于 Edge TTS 的语音合成代理服务，专为「We Are Over」游戏设计，支持 33 种情绪（17女+16男）、4 种女声类型、20 个场景映射，提供高质量的年轻男女恋人语音合成。

**服务地址**: `http://localhost:3001`
**API 版本**: v1.0
**认证方式**: Token 查询参数

---

## 认证

所有 API 接口（除健康检查和管理端登录外）都需要在 URL 中携带 `token` 参数：

```
?token=girlforlove
```

**默认 Token**: `girlforlove`（可在数据库 `system_config` 表中修改）

---

## 接口列表

### 1. 健康检查

```
GET /api/health
```

**响应示例**:
```json
{
  "success": true,
  "code": 200,
  "message": "success",
  "data": {
    "status": "ok",
    "service": "TTS Server",
    "audio_count": 10,
    "memory_cache": true
  }
}
```

---

### 2. 单条语音合成

```
GET /api/tts?token={token}&text={text}&gender={gender}&voice={voice}&emotion={emotion}&scene={scene}&rate={rate}&pitch={pitch}&volume={volume}&format={format}&nocache={nocache}
```

#### 请求参数

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| token | string | 是 | - | 认证 Token |
| text | string | 是 | - | 合成文本（最大 5000 字） |
| gender | string | 否 | female | 性别：`female` / `male` |
| voice | string | 否 | 按场景映射 | 语音类型，见下方列表 |
| emotion | string | 否 | neutral | 情绪类型，见下方列表 |
| scene | string | 否 | - | 场景 ID，用于自动映射语音类型 |
| rate | string | 否 | 自动计算 | 语速，如 `+10%` / `-5%` |
| pitch | string | 否 | 自动计算 | 音调，如 `+20Hz` / `-10Hz` |
| volume | string | 否 | 自动计算 | 音量，如 `+10%` / `-5%` |
| format | string | 否 | json | 响应格式：`json` / `raw` |
| nocache | string | 否 | 0 | 是否跳过缓存：`1` / `0` |

#### 语音类型列表

| 类型 | 说明 | 性别 | Edge TTS 语音 |
|------|------|------|---------------|
| female_sweet | 甜美少女 | 女 | zh-CN-XiaoxiaoNeural |
| female_innocent | 清纯邻家 | 女 | zh-CN-XiaoyiNeural |
| female_cool | 冷艳知性 | 女 | zh-CN-XiaomoNeural |
| female_mature | 御姐成熟 | 女 | zh-CN-XiaohanNeural |
| male_handsome | 阳光男神 | 男 | zh-CN-YunyangNeural |
| male_gentle | 温柔儒雅 | 男 | zh-CN-YunxiNeural |
| male_mature | 成熟磁性 | 男 | zh-CN-YunjianNeural |

#### 女生情绪列表（17种）

`casual` 随意自然, `tired` 疲惫慵懒, `curious` 好奇上扬, `angry` 生气, `furious` 暴怒, `sad` 难过, `hurt` 受伤哽咽, `disappointed` 失望, `shocked` 震惊, `disgusted` 厌恶, `asking` 询问, `hopeful` 期待, `happy` 开心, `explosive` 爆发嘶吼, `breakup_rage` 分手怒吼

#### 男生情绪列表（16种）

`casual` 随意自然, `phone` 心不在焉, `nervous` 紧张, `defensive` 辩解, `dismissive` 不屑, `annoyed` 烦躁, `impatient` 不耐烦, `confused` 困惑, `clueless` 茫然, `indifferent` 冷漠, `complaining` 抱怨, `distracted` 分心, `pain` 疼痛, `beg` 求饶, `breakdown` 崩溃, `numb` 麻木

#### 场景列表（20个）

`bedroom` 卧室, `bathroom` 卫生间, `bathroom_shower` 浴室, `living_room` 客厅, `kitchen` 厨房, `balcony` 阳台, `study` 书房, `restaurant` 餐厅, `cinema` 电影院, `mall` 商场, `car` 车里, `subway` 地铁, `park` 公园, `hotel` 酒店, `friends_party` 朋友聚会, `meet_parents` 见家长, `wedding` 婚礼, `hospital` 医院, `gym` 健身房, `supermarket` 超市

#### 响应示例（JSON 格式）

```json
{
  "success": true,
  "code": 200,
  "message": "success",
  "data": {
    "audio": "data:audio/mpeg;base64,SUQzBAAAAAA...",
    "audio_id": 1,
    "cache_key": "a1b2c3d4e5f6...",
    "voice_type": "female_sweet",
    "voice_name": "zh-CN-XiaoxiaoNeural",
    "emotion": "happy",
    "rate": "+17%",
    "pitch": "+39Hz",
    "volume": "+5%",
    "gender": "female",
    "file_size": 6624,
    "duration_ms": 1200,
    "cached": false,
    "cache_level": "edge_tts"
  }
}
```

#### 响应示例（RAW 格式）

直接返回 MP3 音频流，`Content-Type: audio/mpeg`，响应头包含：
- `X-Cache`: HIT / MISS
- `X-Cache-Level`: memory / disk / edge_tts

---

### 3. 批量语音合成

```
POST /api/tts/batch?token={token}
Content-Type: application/json
```

#### 请求体

```json
{
  "items": [
    {
      "text": "你好啊",
      "gender": "female",
      "voice": "female_sweet",
      "emotion": "happy",
      "scene": "bedroom",
      "rate": "+10%",
      "pitch": "+20Hz",
      "volume": "+5%"
    },
    {
      "text": "怎么了",
      "gender": "male",
      "emotion": "casual"
    }
  ]
}
```

**限制**: 单次最多 50 条

#### 响应示例

```json
{
  "success": true,
  "code": 200,
  "message": "success",
  "data": {
    "total": 2,
    "success": 2,
    "items": [
      {
        "text": "你好啊",
        "gender": "female",
        "voice_type": "female_sweet",
        "voice_name": "zh-CN-XiaoxiaoNeural",
        "emotion": "happy",
        "rate": "+17%",
        "pitch": "+39Hz",
        "volume": "+5%",
        "audio": "data:audio/mpeg;base64,...",
        "audio_id": 1,
        "file_size": 6624,
        "duration_ms": 1200,
        "cached": false,
        "error": null
      },
      {
        "text": "怎么了",
        "gender": "male",
        "voice_type": "male_handsome",
        "voice_name": "zh-CN-YunyangNeural",
        "emotion": "casual",
        "rate": "-8%",
        "pitch": "-22Hz",
        "volume": "-10%",
        "audio": "data:audio/mpeg;base64,...",
        "audio_id": 2,
        "file_size": 7632,
        "duration_ms": 1500,
        "cached": false,
        "error": null
      }
    ]
  }
}
```

---

### 4. 元数据查询

```
GET /api/tts/metadata?token={token}
```

#### 响应示例

```json
{
  "success": true,
  "code": 200,
  "message": "success",
  "data": {
    "version": "1.0",
    "emotions": {
      "female": [
        {
          "id": 1,
          "gender": "female",
          "emotion": "happy",
          "label": "开心",
          "pitch": 1.22,
          "rate": 1.08,
          "volume": 1.05
        }
      ],
      "male": [...]
    },
    "voice_types": [
      {
        "id": 1,
        "voice_type": "female_sweet",
        "label": "甜美少女",
        "gender": "female",
        "edge_tts_voice": "zh-CN-XiaoxiaoNeural",
        "base_pitch": 1.45,
        "base_rate": 1.14,
        "base_volume": 1.0
      }
    ],
    "scene_mappings": [
      {
        "id": 1,
        "scene_id": "bedroom",
        "label": "卧室",
        "female_voice_type": "female_sweet",
        "male_voice_type": "male_handsome",
        "default_emotion": "casual"
      }
    ],
    "defaults": {
      "gender": "female",
      "emotion": "neutral",
      "voice": "female_sweet",
      "max_text_len": 5000,
      "max_batch_items": 50
    }
  }
}
```

---

### 5. 音频流播放

```
GET /api/audios/:id/stream?token={token}
```

支持 HTTP Range 请求，可用于音频播放器的进度拖动。

#### 响应头

- `Content-Type: audio/mpeg`
- `Accept-Ranges: bytes`
- `Content-Range: bytes 0-1023/6624`（部分内容时）
- `Cache-Control: public, max-age=86400`

---

## 数据协议规范

### 1. 统一响应格式

所有接口返回统一的 JSON 格式：

```json
{
  "success": true,
  "code": 200,
  "message": "success",
  "data": {}
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| success | boolean | 请求是否成功 |
| code | int | HTTP 状态码 |
| message | string | 状态消息 |
| data | object | 响应数据（失败时为 null） |

### 2. 错误响应格式

```json
{
  "success": false,
  "code": 400,
  "message": "text 参数不能为空",
  "data": null
}
```

### 3. 错误码

| 状态码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未授权（Token 无效） |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |

### 4. 音频数据格式

- **格式**: MP3 (MPEG-1 Audio Layer III)
- **编码**: base64（JSON 响应时）
- **Data URI**: `data:audio/mpeg;base64,{base64_data}`
- **采样率**: 24kHz（Edge TTS 默认）
- **比特率**: 约 48kbps

### 5. 参数计算规则

#### 女声参数计算

```
最终参数 = 语音类型基础参数 + (情绪参数 - 少女基准参数)
```

- 少女基准: pitch=1.28, rate=1.05, volume=1.0
- 范围限制: pitch [0,2], rate [0.1,10], volume [0,2]

#### 男声参数计算

```
最终参数 = 情绪参数（直接使用）
```

#### Web Speech → Edge TTS 转换

| 参数 | Web Speech 范围 | Edge TTS 格式 | 转换公式 |
|------|----------------|---------------|----------|
| pitch | 0-2（默认1） | `+/-N Hz` | `(pitch - 1.0) * 100` |
| rate | 0.1-10（默认1） | `+/-N %` | `(rate - 1.0) * 100` |
| volume | 0-2（默认1） | `+/-N %` | `(volume - 1.0) * 100` |

### 6. 缓存机制

三级缓存，相同请求（text+voice+emotion+rate+pitch+volume）优先使用缓存：

1. **L1 内存缓存**: LRU，最快，服务重启后清空
2. **L2 数据库+磁盘**: SQLite 记录元数据 + 磁盘 MP3 文件
3. **L3 Edge TTS**: 实时合成，最慢

缓存键: `MD5(text + voiceType + emotion + rate + pitch + volume)`

---

## 前端集成示例

### JavaScript / Vue3

```javascript
// 单条合成
async function speak(text, gender, options = {}) {
  const params = new URLSearchParams({
    token: 'girlforlove',
    text,
    gender,
    ...options
  })
  const res = await fetch(`http://localhost:3001/api/tts?${params}`)
  const data = await res.json()
  if (data.success) {
    const audio = new Audio(data.data.audio)
    await audio.play()
  }
}

// 批量合成（游戏对话预加载）
async function preloadDialogues(dialogues) {
  const res = await fetch('http://localhost:3001/api/tts/batch?token=girlforlove', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ items: dialogues })
  })
  const data = await res.json()
  return data.data.items
}

// 使用示例
speak('我们分手吧', 'female', { emotion: 'breakup_rage', scene: 'bedroom' })
```

---

## 管理端接口

管理端接口需要先登录获取 Session Token，通过 `X-Admin-Token` 请求头认证。

### 登录

```
POST /api/admin/login
Content-Type: application/json

{"password": "admin"}
```

### 元数据管理

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /api/admin/emotion-params?gender=female | 获取情绪参数列表 |
| PUT | /api/admin/emotion-params/:id | 修改情绪参数 |
| GET | /api/admin/voice-types | 获取语音类型列表 |
| PUT | /api/admin/voice-types/:id | 修改语音类型 |
| GET | /api/admin/scene-mappings | 获取场景映射列表 |
| PUT | /api/admin/scene-mappings/:id | 修改场景映射 |

**修改后实时生效，无需重启服务。**

---

## 能力对比

| 能力 | Web Speech API | TTS Server (Edge TTS) |
|------|---------------|----------------------|
| 语音质量 | 取决于系统，差异大 | 微软神经语音，高质量稳定 |
| 情绪支持 | 仅 pitch/rate/volume | 33种情绪预设 + 自动参数计算 |
| 女声类型 | 无 | 4种（清纯/甜美/冷艳/御姐） |
| 场景映射 | 无 | 20场景自动映射 |
| 缓存 | 无 | 三级缓存，重复请求零延迟 |
| 批量合成 | 需逐个调用 | 单次最多50条 |
| 跨平台一致性 | 差（各系统语音不同） | 好（服务端统一合成） |
| 离线可用 | 是 | 否（需服务端） |

---

## 总结

TTS Server 完全满足「We Are Over」游戏的语音需求：

- ✅ 33种情绪（17女+16男）
- ✅ 4种女声类型（清纯/甜美/冷艳/御姐）
- ✅ 20个场景自动映射
- ✅ 男女声区分（14-18岁少女 + 男神音）
- ✅ 批量合成（游戏对话预加载）
- ✅ 三级缓存（高性能）
- ✅ 元数据可配置（管理端实时修改）
- ✅ 标准化接口（RESTful + 统一响应格式）
