Files
blind/执行文档_音频素材制作.md
T
2026-05-18 18:39:49 +08:00

714 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 《林夏》音频素材本地制作执行文档
> **前置文档**:`林夏.md`(游戏设计)、`SOP_AI音频生产.md`(生产规范)
> **执行环境**:本机 WSL2 · RTX 5090 32GB · CosyVoice2-0.5B · FFmpeg 7.0
> **目标**:按 SOP 规范在本地生产全部音频素材
---
## 0. 本机资源清单
| 资源 | 路径 / 状态 | 用途 |
|---|---|---|
| CosyVoice 2.0 (0.5B) | `/home/xsl/tools/CosyVoice` · conda env: `cosyvoice` | TTS 主力(替代 SOP 中 MiniMax/豆包方案) |
| FFmpeg 7.0 | `/usr/local/bin/ffmpeg` | 音频格式转换、响度归一化、听筒效果 |
| GPU | RTX 5090 32GB | 跑 CosyVoice 推理 |
| MuseTalk | `/home/xsl/work/MuseTalk` | **注意:这是唇形同步工具,不是 TTS** |
> **SOP 中的 MiniMax / 豆包 API 全部替换为本地 CosyVoice**。音乐(Suno)、音效(ElevenLabs SFX)、环境采样(Freesound)仍走云端。
---
## 1. 环境部署
### 1.1 创建项目目录结构
```bash
cd /home/xsl/blind
mkdir -p audio/{00_raw/{tts,music,sfx,ambience,ringtone},01_processed/{tts,music,sfx,ambience,ringtone},02_final/{tts,music,sfx,ambience,ringtone},03_qa_reports,99_legal}
mkdir -p audio/00_raw/tts/prompt_ref # 存放角色参考音频
```
### 1.2 验证 CosyVoice 环境
```bash
# 激活 conda 环境
conda activate cosyvoice
# 验证依赖
cd /home/xsl/tools/CosyVoice
python -c "
import sys
sys.path.append('third_party/Matcha-TTS')
from cosyvoice.cli.cosyvoice import CosyVoice2
model = CosyVoice2('pretrained_models/CosyVoice2-0.5B')
print('可用预训练音色:', model.list_available_spks())
print('采样率:', model.sample_rate)
print('CosyVoice2 加载成功')
"
```
如果报错 `ModuleNotFoundError`,先装依赖:
```bash
conda activate cosyvoice
cd /home/xsl/tools/CosyVoice
pip install -r requirements.txt
```
### 1.3 启动 CosyVoice WebUI
```bash
conda activate cosyvoice
cd /home/xsl/tools/CosyVoice
python webui.py --port 8000
```
浏览器访问 `http://localhost:8000`。WebUI 提供 4 种推理模式:
| 模式 | 说明 | 本项目用途 |
|---|---|---|
| **预训练音色** | 从内置音色列表选择 | 快速试听、系统 TTS |
| **3s极速复刻** | 提供 3-30s 参考音频 + 对应文字 | **主力模式**:为每个角色准备参考音频 |
| **跨语种复刻** | 跨语言克隆 | 暂不需要 |
| **自然语言控制** | 用文字描述控制语气风格 | CosyVoice2 不支持此模式,改用 instruct2 API |
### 1.4 验证 FFmpeg
```bash
ffmpeg -version | head -1
# 预期输出: ffmpeg version 7.0.2-static
# 测试 loudnorm 滤镜
ffmpeg -f lavfi -i "sine=frequency=440:duration=1" -af "loudnorm=I=-16:TP=-1:LRA=11" -y /tmp/test_loudnorm.wav
echo "FFmpeg loudnorm 可用"
```
---
## 2. 角色参考音频准备(关键步骤)
CosyVoice2 的"3s极速复刻"需要**每个角色一段 5-20 秒的参考音频**。参考音频的音色决定了最终 TTS 的音色。
### 2.1 参考音频来源方案
| 角色 | 音色要求 | 参考音频获取方案 |
|---|---|---|
| `mom` 妈妈 | 50 岁,温柔克制 | 方案 A:找一位 45-55 岁女性朋友录 15 秒朗读(最佳);方案 B:从影视作品中截取合适片段 |
| `azhe` 阿哲 | 27 岁,理性偏冷 | 从播客/有声书截取年轻男声冷淡片段 |
| `xiaomei` 小美 | 24 岁,活泼 | 找年轻女性朋友录制,或从 vlog 截取 |
| `zhounan` 周南 | 26 岁,温和拘谨 | 播客/有声书截取温和男声 |
| `hr` HR 王姐 | 38 岁,知性 | 新闻女主播或播客截取 |
| `anan` 安安 | 23 岁,可爱 | 年轻女性活泼语调截取 |
| `dorm-a/b/c` | 三个不同年轻女声 | 需要 3 段风格不同的女声参考 |
| `delivery` 外卖小哥 | 30 岁,北方腔 | 短视频/影视截取 |
| `system` 系统 | 中性 AI 女声 | 用 CosyVoice 预训练音色即可 |
| `self-3y` 3年前自己 | 同 xiaomei 基底 | 复用 xiaomei 参考音频,调速度和音调 |
### 2.2 参考音频制作规格
- **时长**:5-20 秒(推荐 10 秒左右)
- **格式**WAV16kHz 以上采样率
- **内容**:完整的中文句子(需同时记录对应文字)
- **质量**:干声为主,避免底噪、混响、BGM
存放路径:`audio/00_raw/tts/prompt_ref/{角色代号}_ref.wav`
```bash
# 如果参考音频不是 wav 或采样率不对,用 FFmpeg 转换
ffmpeg -i input.mp3 -ar 16000 -ac 1 audio/00_raw/tts/prompt_ref/mom_ref.wav
```
### 2.3 创建参考音频记录表
`audio/00_raw/tts/prompt_ref/` 下创建 `ref_manifest.txt`,记录每条参考音频对应的文字:
```
mom_ref.wav | 今天天气不错,妈妈给你做了红烧排骨,等你回来吃饭。
azhe_ref.wav | 好的,我知道了,那件事明天再说吧。
xiaomei_ref.wav | 哎你猜怎么着,今天路上遇到一个超级搞笑的事!
zhounan_ref.wav | 嗯,其实我也不太确定,就是觉得应该跟你说一声。
hr_ref.wav | 好的,那我们下周三前把流程走完,有什么问题随时联系我。
anan_ref.wav | 天呐天呐你快来!这边超级好玩的!
dorm_a_ref.wav | 嘿你们看看我,今天升职了!请大家吃饭!
dorm_b_ref.wav | 恭喜恭喜啊,太厉害了,我就知道你行的。
dorm_c_ref.wav | 嗯,不错,对了夏夏怎么不说话啊。
delivery_ref.wav | 你好你的外卖到了,放门口了啊,麻辣烫注意别洒了。
```
---
## 3. TTS 生产流程
### 3.1 用 WebUI 逐条生产(推荐新手)
1. 浏览器打开 `http://localhost:8000`
2. 选择模式:**3s极速复刻**
3. 上传对应角色的参考音频
4. 在"prompt文本"中输入参考音频对应的文字
5. 在"合成文本"中输入要合成的台词
6. 调整速度(对应 SOP §4.2 各角色 Speed 值)
7. 点击"生成音频"
8. 试听满意后,右键保存音频
### 3.2 用 Python 脚本批量生产(推荐熟练后)
创建批量生产脚本 `audio/batch_tts.py`
```python
#!/usr/bin/env python3
"""
《林夏》TTS 批量生产脚本
用法: conda activate cosyvoice && python audio/batch_tts.py
"""
import os, sys, json, torch, torchaudio
# 设置路径
COSYVOICE_DIR = "/home/xsl/tools/CosyVoice"
PROJECT_DIR = "/home/xsl/blind"
sys.path.insert(0, COSYVOICE_DIR)
sys.path.insert(0, f"{COSYVOICE_DIR}/third_party/Matcha-TTS")
from cosyvoice.cli.cosyvoice import CosyVoice2
from cosyvoice.utils.file_utils import load_wav
# 加载模型
print("正在加载 CosyVoice2 模型...")
model = CosyVoice2(f"{COSYVOICE_DIR}/pretrained_models/CosyVoice2-0.5B")
print(f"模型加载完成,采样率: {model.sample_rate}")
# ============================================================
# 角色配置 — 按 SOP §4.2 配置卡填写
# speed: 对应 SOP 中的 Speed 值
# ref_wav: 参考音频路径
# ref_text: 参考音频对应文字
# ============================================================
ROLES = {
"mom": {
"speed": 0.92,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/mom_ref.wav",
"ref_text": "今天天气不错,妈妈给你做了红烧排骨,等你回来吃饭。",
},
"azhe": {
"speed": 1.05,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/azhe_ref.wav",
"ref_text": "好的,我知道了,那件事明天再说吧。",
},
"xiaomei": {
"speed": 1.10,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/xiaomei_ref.wav",
"ref_text": "哎你猜怎么着,今天路上遇到一个超级搞笑的事!",
},
"zhounan": {
"speed": 0.95,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/zhounan_ref.wav",
"ref_text": "嗯,其实我也不太确定,就是觉得应该跟你说一声。",
},
"hr": {
"speed": 1.00,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/hr_ref.wav",
"ref_text": "好的,那我们下周三前把流程走完,有什么问题随时联系我。",
},
"anan": {
"speed": 1.15,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/anan_ref.wav",
"ref_text": "天呐天呐你快来!这边超级好玩的!",
},
"delivery": {
"speed": 1.20,
"ref_wav": f"{PROJECT_DIR}/audio/00_raw/tts/prompt_ref/delivery_ref.wav",
"ref_text": "你好你的外卖到了,放门口了啊,麻辣烫注意别洒了。",
},
# system 用预训练音色,不走 zero-shot
}
# ============================================================
# 台词表 — 从林夏.md §3 / §5 抄写
# 每条格式: (文件名前缀, 角色, 台词, speed_override=None)
# ============================================================
LINES = [
# === 第 01 条 · 21:03 妈妈电话 ===
("lv11_2103_mom_call_01", "mom",
"今晚煮了你爱吃的排骨,给你拍张照啊。下周五回来一趟,你高中物理老师的儿子,挺好的。"),
# === 第 02 条 · 21:15 小美三连 ===
("lv11_2115_xiaomei_voice_01", "xiaomei", "在吗"),
("lv11_2115_xiaomei_voice_02", "xiaomei", "你被甩了我都听说了"),
("lv11_2115_xiaomei_voice_03", "xiaomei", "明天晚上喝酒,海底捞旁边那家,不许放鸽子"),
# === 第 03 条 · 21:20 外卖小哥 ===
("lv11_2120_delivery_call_01", "delivery",
"你好你的麻辣烫到了,放门口可以吗?"),
# === 第 04 条 · 21:30 阿哲语音 ===
("lv11_2130_azhe_voice_01", "azhe",
"嗯……夏夏。我那个AirPods充电盒,好像落你抽屉里了。你方便的时候帮我寄一下吗,工位还要用。地址我等下发你。"),
# === 第 05 条 · 21:45 HR 王姐 ===
("lv11_2145_hr_voice_01", "hr",
"夏夏啊,离职流程你下周三之前办完哈,公积金那边记得跟进。辛苦啦,外面有合适的工作我帮你留意。"),
# === 第 06 条 · 22:00 安安 ===
("lv11_2200_anan_voice_01", "anan",
"夏夏!我在helens!要不要过来!我请客!"),
# === 第 10 条 · 23:00 妈妈文字 ===
("lv11_2300_mom_text_01", "mom", "睡了吗?"),
# === 第 11 条 · 23:15 阿哲电话 ===
("lv11_2315_azhe_call_01", "azhe",
"我能过去拿吗?就5分钟"),
# === 第 12 条 · 23:30 周南 ===
("lv11_2330_zhounan_voice_01", "zhounan",
"林夏,是我,周南。你应该不记得我了吧,初三坐你后桌那个"),
# === 第 14 条 · 00:15 妈妈电话 ===
("lv11_0015_mom_call_01", "mom",
"夏夏,你不接电话我有点担心。妈妈知道你工作忙,你早点睡。"),
# ... 更多台词按需添加
# 长戏(妈妈 4'17"、小美醉语音、周南 4 段)需分段处理,见 §3.3
]
def synthesize_line(filename, role, text, speed_override=None):
"""合成单条台词"""
cfg = ROLES[role]
speed = speed_override or cfg["speed"]
ref_wav = cfg["ref_wav"]
ref_text = cfg["ref_text"]
if not os.path.exists(ref_wav):
print(f" [跳过] 参考音频不存在: {ref_wav}")
return
# 加载参考音频
prompt_speech = load_wav(ref_wav, 16000)
# 裁剪到合理长度
if prompt_speech.shape[1] > 16000 * 30:
prompt_speech = prompt_speech[:, :16000 * 30]
output_path = f"{PROJECT_DIR}/audio/00_raw/tts/{filename}_v1.wav"
print(f" 合成中: {filename} [{role}] speed={speed}")
print(f" 台词: {text[:50]}...")
speeches = []
for chunk in model.inference_zero_shot(
tts_text=text,
prompt_text=ref_text,
prompt_speech_16k=prompt_speech,
stream=False,
speed=speed,
):
speeches.append(chunk["tts_speech"])
if speeches:
full_speech = torch.concat(speeches, dim=1)
torchaudio.save(output_path, full_speech, model.sample_rate)
duration = full_speech.shape[1] / model.sample_rate
print(f" 已保存: {output_path} ({duration:.1f}s)")
else:
print(f" [失败] 未生成音频: {filename}")
def main():
print(f"\n{'='*60}")
print(f" 《林夏》TTS 批量生产")
print(f" 共 {len(LINES)} 条台词")
print(f"{'='*60}\n")
for i, line in enumerate(LINES):
filename = line[0]
role = line[1]
text = line[2]
speed_override = line[3] if len(line) > 3 else None
print(f"\n[{i+1}/{len(LINES)}] {filename}")
synthesize_line(filename, role, text, speed_override)
print(f"\n{'='*60}")
print(" 批量合成完成!请到 audio/00_raw/tts/ 检查结果")
print(f"{'='*60}")
if __name__ == "__main__":
main()
```
### 3.3 长戏分段处理
SOP §5.3 规定:**超过 30 秒的台词必须分段录制**。
**妈妈 4'17" 长语音(第 16 条)分 8 段**
| 段号 | 文件名 | 台词 | 情绪提示 |
|---|---|---|---|
| 1 | `lv11_0200_mom_voice_01_seg1` | "夏夏。妈妈知道你今天电话不接。妈妈不催你,就是想跟你说一件事。" | gentle, hesitant |
| 2 | `lv11_0200_mom_voice_01_seg2` | "你24了对吧。妈妈24那年——你应该没听我说过——" | gentle, nostalgic |
| 3 | `lv11_0200_mom_voice_01_seg3` | "那时候妈妈在广州,给一个香港老板做秘书,1995年。那年我也被分手过。也被开除过。一模一样的春天。" | gentle, sad but holding back |
| 4 | `lv11_0200_mom_voice_01_seg4` | "那年我哭得特别难看。后来呢?后来就没什么后来了。我回了老家,认识了你爸,生了你。" | gentle, releasing |
| 5 | `lv11_0200_mom_voice_01_seg5` | "我跟你说这个不是想跟你说什么都会过去——我想跟你说,那年夏天我没死,是因为我外婆每天晚上都给我打一个电话。" | gentle, emotional |
| 6 | `lv11_0200_mom_voice_01_seg6` | "她不说什么,就问我今天吃饭了没。" | gentle, peaceful |
| 7 | `lv11_0200_mom_voice_01_seg7` | "我现在打给你,你不接也没事。妈妈给你发个语音,你什么时候想听就听。你不用回我。" | gentle, tender |
| 8 | `lv11_0200_mom_voice_01_seg8` | "你今天接了好多电话吧。妈妈不打扰你了。晚安。" | gentle, almost smiling |
每段单独合成后用 FFmpeg 拼接(见 §4.3)。
**小美醉语音(第 09 条)分 4 段**,speed 调低到 0.95,每段间加长停顿。
**周南 4 段长讲述(第 13 条)** 每段独立合成,段间留 800ms 静音。
### 3.4 系统 TTS
系统提示音(播报未读消息、念选项等)用 CosyVoice **预训练音色**模式:
1. WebUI 选"预训练音色"
2. 从下拉菜单选一个清晰的中性女声
3. 合成所有系统语句
系统语句示例:
- "阿哲,发来1条语音。8秒。"
- "是否回复?"
- "一,好,明天寄。二,你怎么不自己来拿。三,你那边都好吗。四,什么都不说。"
- "妈妈3条未读、小美1条已读、阿哲1条未播放"
### 3.5 self-3y 三版预录
使用与 `xiaomei` 同基底参考音频,但调整 speed 为 +0.05(即 1.15):
| 版本 | 文件名 | 内容要求 | 情绪 |
|---|---|---|---|
| 明朗版 | `lv11_0030_self-3y_voice_01` | 3 年前刚来北京,对一切新鲜 | 兴奋带紧张 |
| 迷茫版 | `lv11_0030_self-3y_voice_02` | 3 年前已经累了 | 假装坚强 |
| 温柔版 | `lv11_0030_self-3y_voice_03` | 写给未来的自己 | 平静有力 |
> 台词需要编剧写好,每版约 50 秒。
---
## 4. 后期处理(FFmpeg
### 4.1 单条 TTS 标准处理链
```bash
# 变量
INPUT="audio/00_raw/tts/lv11_2103_mom_call_01_v1.wav"
OUTPUT="audio/01_processed/tts/lv11_2103_mom_call_01_v1.wav"
# 完整处理链(一条命令):
# [1] 高通 80Hz 去低频噪
# [2] 压缩器 4:1
# [3] 响度归一化到 -16 LUFS
# [4] 限制峰值 -1 dBTP
ffmpeg -i "$INPUT" \
-af "highpass=f=80,\
acompressor=threshold=-18dB:ratio=4:attack=20:release=250,\
loudnorm=I=-16:TP=-1:LRA=11,\
alimiter=limit=0.891" \
-ar 48000 -sample_fmt s16 -ac 1 \
-y "$OUTPUT"
```
### 4.2 加听筒效果(电话 / 微信语音形态)
```bash
INPUT="audio/01_processed/tts/lv11_2103_mom_call_01_v1.wav"
OUTPUT="audio/01_processed/tts/lv11_2103_mom_call_01_v1_phone.wav"
# 模拟手机听筒: HPF 300Hz + LPF 3.4kHz + 轻微饱和
ffmpeg -i "$INPUT" \
-af "highpass=f=300:poles=2,\
lowpass=f=3400:poles=2,\
equalizer=f=1500:t=q:w=1:g=1,\
acompressor=threshold=-20dB:ratio=3:attack=5:release=50" \
-y "$OUTPUT"
```
> **例外**`mom` 第 16 条 4'17" 长语音**不加听筒效果**——SOP 设计意图是"完整音质版"。
### 4.3 长戏分段拼接
```bash
# 1. 先给每段加 200ms 静音头尾
for seg in audio/00_raw/tts/lv11_0200_mom_voice_01_seg*.wav; do
ffmpeg -i "$seg" \
-af "adelay=200|200,apad=pad_dur=0.2" \
-y "${seg%.wav}_padded.wav"
done
# 2. 生成拼接文件列表
ls audio/00_raw/tts/lv11_0200_mom_voice_01_seg*_padded.wav | \
sed "s/^/file '/" | sed "s/$/'/" > /tmp/concat_list.txt
# 3. 拼接 + crossfade
ffmpeg -f concat -safe 0 -i /tmp/concat_list.txt \
-af "acrossfade=d=0.05:c1=tri:c2=tri" \
-y audio/00_raw/tts/lv11_0200_mom_voice_01_full.wav
# 注意:FFmpeg concat + acrossfade 只支持两个输入。
# 多段拼接需要逐段 crossfade,或直接 concat 后人工检查接缝。
# 推荐方案:先 concat 拼接,段间 200ms 静音已经足够自然。
ffmpeg -f concat -safe 0 -i /tmp/concat_list.txt \
-c copy \
-y audio/00_raw/tts/lv11_0200_mom_voice_01_full.wav
```
### 4.4 批量后期处理脚本
```bash
#!/bin/bash
# 批量处理 00_raw/tts/ → 01_processed/tts/
# 用法: bash audio/process_tts.sh
for f in audio/00_raw/tts/lv11_*.wav; do
basename=$(basename "$f")
output="audio/01_processed/tts/$basename"
# 跳过已处理的
[ -f "$output" ] && echo "跳过: $basename" && continue
echo "处理: $basename"
ffmpeg -i "$f" \
-af "highpass=f=80,\
acompressor=threshold=-18dB:ratio=4:attack=20:release=250,\
loudnorm=I=-16:TP=-1:LRA=11" \
-ar 48000 -sample_fmt s16 -ac 1 \
-y "$output" 2>/dev/null
echo " → $output"
done
echo "TTS 后期处理完成"
```
### 4.5 最终打包(wav → ogg
```bash
#!/bin/bash
# 01_processed → 02_final (ogg Vorbis q=5)
for f in audio/01_processed/tts/*.wav; do
basename=$(basename "${f%.wav}.ogg")
output="audio/02_final/tts/$basename"
ffmpeg -i "$f" -c:a libvorbis -q:a 5 -y "$output" 2>/dev/null
done
echo "OGG 打包完成"
```
### 4.6 角色专属后期(按 SOP §4.2 配置卡)
```bash
# mom: 高频衰减 -3dB + 轻 reverb
ffmpeg -i input.wav -af "equalizer=f=5000:t=h:w=2000:g=-3" -y output.wav
# azhe: 中频衰减 -2dB"闷"感)
ffmpeg -i input.wav -af "equalizer=f=2000:t=q:w=1:g=-2" -y output.wav
# delivery: 低频衰减 -6dB(模拟听筒)
ffmpeg -i input.wav -af "equalizer=f=200:t=h:w=200:g=-6" -y output.wav
# self-3y: 高频衰减 -2dB + 底噪(录音笔质感)
ffmpeg -i input.wav \
-af "equalizer=f=5000:t=h:w=2000:g=-2" \
-y output.wav
# 底噪可以后续叠加一层轻微白噪声
```
---
## 5. 音乐素材(Suno
音乐生产仍走 Suno 云端,按 SOP §6 操作。
### 5.1 操作步骤
1. 登录 Suno(需要 Pro 订阅)
2. 选 Custom Mode
3. 按 SOP §6.2 的 4 段曲 Prompt 生成
4. 每段跑 2 轮(出 4 首候选),选最佳
5. 用 Extend 延长到目标时长
6. 下载 WAV 格式到 `audio/00_raw/music/`
### 5.2 铃声生成
按 SOP §6.3 的 9 个角色铃声 Prompt 在 Suno 生成。
下载到 `audio/00_raw/ringtone/`
### 5.3 音乐后期处理
```bash
# 音乐处理: 削 AI 浊感 + 响度 -18 LUFS
for f in audio/00_raw/music/*.wav; do
basename=$(basename "$f")
ffmpeg -i "$f" \
-af "equalizer=f=350:t=q:w=1.5:g=-2,\
loudnorm=I=-18:TP=-1:LRA=11" \
-ar 48000 \
-y "audio/01_processed/music/$basename" 2>/dev/null
done
```
---
## 6. 音效素材(ElevenLabs SFX
### 6.1 操作步骤
1. 登录 ElevenLabsCreator 订阅,含 Sound Effects
2. 按 SOP §7.2 的 Prompt 逐条生成
3. **每条跑 3 次**,挑最佳
4. 下载后转 WAV
```bash
# ElevenLabs 默认 mp3,转为 wav
for f in audio/00_raw/sfx/*.mp3; do
ffmpeg -i "$f" -ar 48000 -y "${f%.mp3}.wav" && rm "$f"
done
```
### 6.2 音效后期
```bash
# 短音效响度 -14 LUFS
for f in audio/00_raw/sfx/*.wav; do
basename=$(basename "$f")
ffmpeg -i "$f" \
-af "loudnorm=I=-14:TP=-1:LRA=11" \
-ar 48000 \
-y "audio/01_processed/sfx/$basename" 2>/dev/null
done
```
---
## 7. 环境底噪(Freesound
### 7.1 操作步骤
1. 按 SOP §8.1 的清单在 Freesound 搜索
2. 过滤:CC0 优先,≥ 44.1kHz,下载 WAV
3. 文件名加 Freesound ID:如 `amb_apartment_night_fs123456.wav`
4. **立刻登记到版权表**
### 7.2 环境底噪后期
```bash
# 环境底噪处理: HPF 60Hz + LPF 12kHz + 响度 -22 LUFS
for f in audio/00_raw/ambience/*.wav; do
basename=$(basename "$f")
ffmpeg -i "$f" \
-af "highpass=f=60,lowpass=f=12000,\
loudnorm=I=-22:TP=-3:LRA=11" \
-ar 48000 -ac 2 \
-y "audio/01_processed/ambience/$basename" 2>/dev/null
done
```
### 7.3 单声道转假性立体声
```bash
# 如果下载到的是单声道,做假性立体声
INPUT="audio/01_processed/ambience/amb_apartment_night.wav"
OUTPUT="audio/01_processed/ambience/amb_apartment_night_stereo.wav"
ffmpeg -i "$INPUT" \
-filter_complex "\
[0:a]asplit=2[L][R];\
[R]adelay=12|12,equalizer=f=8000:t=q:w=1:g=1[Rd];\
[L][Rd]amerge=inputs=2[out]" \
-map "[out]" -ac 2 \
-y "$OUTPUT"
```
---
## 8. QA 验收
### 8.1 响度检查脚本
```bash
#!/bin/bash
# 检查所有已处理音频的响度
echo "=== TTS 响度检查 (目标 -16 LUFS) ==="
for f in audio/01_processed/tts/*.wav; do
result=$(ffmpeg -i "$f" -af "loudnorm=print_format=json" -f null - 2>&1 | \
grep -A1 '"input_i"' | tail -1 | tr -d ' ",' | cut -d: -f2)
echo " $(basename "$f"): ${result} LUFS"
done
echo ""
echo "=== 音乐响度检查 (目标 -18 LUFS) ==="
for f in audio/01_processed/music/*.wav; do
result=$(ffmpeg -i "$f" -af "loudnorm=print_format=json" -f null - 2>&1 | \
grep -A1 '"input_i"' | tail -1 | tr -d ' ",' | cut -d: -f2)
echo " $(basename "$f"): ${result} LUFS"
done
echo ""
echo "=== 环境底噪响度检查 (目标 -22 LUFS) ==="
for f in audio/01_processed/ambience/*.wav; do
result=$(ffmpeg -i "$f" -af "loudnorm=print_format=json" -f null - 2>&1 | \
grep -A1 '"input_i"' | tail -1 | tr -d ' ",' | cut -d: -f2)
echo " $(basename "$f"): ${result} LUFS"
done
```
### 8.2 验收流程
1. **自检**:跑响度检查脚本,确认所有文件在目标范围 ±1 LUFS
2. **单条听感**:用耳机逐条听,标记不满意的(记录到 `03_qa_reports/`
3. **上下文听感**:把同一时间段的所有音频按时间轴拼起来完整听一遍
4. **不满意的重做**:回到 §3 重新合成,调参或换参考音频
---
## 9. 执行顺序总结
```
第 1 步 · 部署环境 (§1)
↓ 约 30 分钟
第 2 步 · 准备参考音频 (§2) ← 这一步最关键,音色质量取决于此
↓ 约 1-3 天(需要找人录音或搜集素材)
第 3 步 · 系统 TTS 先行 (§3.4) ← 最简单的先跑通流程
↓ 约 1 小时
第 4 步 · 短台词批量生产 (§3.1/3.2)
↓ 约 3-4 小时
第 5 步 · 长戏分段录制 (§3.3)
↓ 约 2-3 小时
第 6 步 · 后期处理 (§4)
↓ 约 2-3 小时
第 7 步 · 铃声 + 音乐 (§5) ← 可与 TTS 并行
↓ 约 3 小时
第 8 步 · 音效 (§6) ← 可与 TTS 并行
↓ 约 3 小时
第 9 步 · 环境底噪 (§7) ← 可与 TTS 并行
↓ 约 2 小时
第 10 步 · QA 验收 (§8)
↓ 约 4-6 小时
第 11 步 · 最终打包 (§4.5)
↓ 约 30 分钟
完成
```
---
## 附录 A · 常见问题
**Q: CosyVoice 生成的语音有电流声/杂音?**
A: 检查参考音频质量。参考音频必须干净无噪,采样率 ≥ 16kHz。
**Q: 语速不自然?**
A: speed 参数范围 0.5-2.0。如果调速后音质下降,改为在合成文本中加标点控制节奏(句号=长停顿,逗号=短停顿)。
**Q: 怎样让同一个音色听起来"醉了"**
A: CosyVoice 没有情绪控制参数。替代方案:
1. 在文本中插入省略号、重复字来模拟口齿不清
2. 后期用 FFmpeg 轻微变速+加混响:
```bash
ffmpeg -i drunk.wav -af "atempo=0.95,aecho=0.8:0.7:40:0.3" -y drunk_fx.wav
```
**Q: WebUI 启动报 CUDA 错误?**
A: 确认 conda env 正确:`conda activate cosyvoice`。确认 GPU 可用:`nvidia-smi`。
**Q: 生成很慢?**
A: RTX 5090 应该很快(远超实时)。如果慢,检查是否误用了 CPU。在 Python 中加 `print(torch.cuda.is_available())` 确认。