跳到主内容
快讯直播
AI智模界
教程

Hurston-1 本地部署:口音英语 TTS 与长文分块

Hurston-1 是一类支持多说话人、多口音英语合成的开源 TTS 项目。把它跑在本地,好处是音频不出内网、可以批量跑长文、可以针对口音做细粒度挑选。这篇教程按"装起来 → 挑音色 → 处理长文 → 拼接调优"的顺序走一遍,命令尽量写成可以直接照抄的形式,涉及具体版本号与权重名的地方,一律以官方文档当前版本为准。

适用场景

适合需要批量生成带地域口音英语语音的场景,比如播客旁白、有声读物、课程配音、客服话术试听。也适合数据不能离开内网、或者需要对同一段文本反复对比不同口音的团队。如果你只是偶尔合成一两句话,直接用在线 API 更省事,本地部署的价值在于可控、可批量、可复现。

环境与前置条件

  • 操作系统:Linux(Ubuntu 一类的主流发行版)最省心;macOS 可用 CPU 或 Apple Silicon 加速;Windows 建议走 WSL2,避免路径与依赖问题。
  • Python:3.10 及以上,具体下限看项目 pyproject.toml 或 requirements.txt。
  • 内存:建议 16 GB 起。加载模型时会有明显的瞬时峰值。
  • 显存:CPU 也能推理,但长文会明显变慢。有 NVIDIA GPU 时建议 8 GB 以上,开启半精度可以进一步降低占用。显存不足时优先缩短单块文本长度。
  • 磁盘:模型权重加缓存预留 10–20 GB。长文合成会产出大量 wav,建议单独挂一个数据盘。
  • 系统依赖:git、git-lfs(权重通常用 LFS 托管)、ffmpeg(读写音频、转码)。

一句话检查:

```bash

python3 --version && git --version && git lfs version && ffmpeg -version | head -n 1

```

四条都有输出即视为环境备齐。

分步骤部署

第 1 步:安装系统依赖

```bash

Debian / Ubuntu 系

sudo apt update

sudo apt install -y git git-lfs ffmpeg libsndfile1 build-essential

git lfs install

macOS

brew install git git-lfs ffmpeg libsndfile

git lfs install

```

libsndfile1 是 soundfile 读写 wav 的底层依赖,缺了会在导入时直接报错。git lfs install 只需执行一次,它是给当前用户装钩子。

第 2 步:创建虚拟环境

```bash

python3 -m venv ~/venvs/hurston

source ~/venvs/hurston/bin/activate

python -m pip install -U pip setuptools wheel

```

所有后续操作都在这个 venv 里进行。装完后 which python 应指向 ~/venvs/hurston/bin/python,这是判断是否真的激活成功的可靠方式。

第 3 步:安装 PyTorch

PyTorch 的版本必须和本机 CUDA 驱动匹配。不要去猜索引地址,直接打开 PyTorch 官方安装页面,按"你的系统 + 包管理器 + CUDA 版本"选出来的那条命令粘贴执行即可。CPU 环境则选 CPU 版本。

装完立刻验证:

```bash

python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

```

输出里 True 表示 GPU 可用。如果是 False 但机器有卡,多半是驱动或 CUDA 版本不匹配,先解决这个再往下走。

第 4 步:获取代码与权重

```bash

cd ~

git clone <Hurston-1 官方仓库地址> hurston-1

cd hurston-1

pip install -r requirements.txt

```

仓库地址、依赖清单和权重下载方式都以官方文档当前版本为准。部分项目会把权重单独发布,需要按 README 的说明下载后放到约定的 checkpoints/ 或 models/ 目录。下载完检查文件大小,如果只有几百字节,基本是 LFS 指针没拉下来,执行:

```bash

git lfs pull

```

第 5 步:跑通最小推理

先不要碰长文,用一句最短的英文确认整条链路通:

```bash

python -m <项目提供的推理入口> \

--text "Hello, this is a short test." \

--voice <音色名> \

--output ./out/test.wav

```

不同项目的入口和参数名不一样,--voice 也可能叫 --speaker、--speaker_id。以 --help 输出为准:

```bash

python -m <推理入口> --help

```

成功标志是 ./out/test.wav 存在且能正常播放。

第 6 步:列出并挑选口音音色

多说话人模型一般会在配置里维护一份音色表,字段通常包含音色名、性别、地区/口音标签。先把表打出来:

```bash

方式一:项目自带的列表命令(推荐)

python -m <推理入口> --list-voices

方式二:直接看配置文件

cat configs/voices.* 2>/dev/null || ls configs/

```

挑口音不要靠标签硬信,做一次"试听矩阵"最靠谱:固定同一句包含易暴露口音的文本,把所有候选音色各跑一遍。

```bash

TEXT="I parked the car down by the river and walked over the bridge."

for v in $(python -m <推理入口> --list-voices --quiet); do

python -m <推理入口> --text "$TEXT" --voice "$v" --output "./audition/${v}.wav"

done

```

试听时重点听三处:元音是否走样(如 parked 的 r 音处理)、句尾语调是否上扬、连读是否自然。把通过初筛的 3–5 个音色记到一张表里,后面长文只用这几个。同一篇长文务必只用一个音色,中途换音色是最容易被听出来的破绽。

第 7 步:长文分块

TTS 模型对单次输入长度有限制,长文必须切。切分原则是在句子边界切,不在句中切,因为模型看到句号才会收尾降调,看到逗号会保持上扬。

```python

split.py

import re

SENT_END = re.compile(r'(?<=[.!?])\s+')

def split_text(text, max_chars=300):

sents = [s.strip() for s in SENT_END.split(text.strip()) if s.strip()]

chunks, cur = [], ""

for s in sents:

if len(cur) + len(s) + 1 <= max_chars:

cur = (cur + " " + s).strip()

continue

if cur:

chunks.append(cur)

单句仍然超长时,退到逗号处切

while len(s) > max_chars:

cut = s.rfind(",", 0, max_chars)

if cut == -1:

cut = max_chars

chunks.append(s[:cut + 1].strip())

s = s[cut + 1:].strip()

cur = s

if cur:

chunks.append(cur)

return chunks

```

max_chars 建议从 200–400 起步,具体上界以模型支持的输入长度为准。块太短会让语调碎,太长会增加显存压力和前后不一致的概率。

调用前先做文本规范化:把数字、货币、缩写、专有名词统一写法定型。同一个 Dr. 在块 A 里读成 "doctor"、块 B 里读成 "drive",拼起来就是明显的瑕疵。

```bash

python - <<'PY'

from split import split_text

text = open("script.txt", encoding="utf-8").read()

chunks = split_text(text, max_chars=300)

for i, c in enumerate(chunks):

open(f"chunks/{i:04d}.txt", "w", encoding="utf-8").write(c)

print(len(chunks), "chunks")

PY

```

第 8 步:批量合成与拼接

```python

synth_and_join.py

import os, subprocess, numpy as np, soundfile as sf

GAP_SENT, GAP_PARA = 0.25, 0.6

def synth(txt_path, wav_path, voice):

text = open(txt_path, encoding="utf-8").read()

subprocess.run([

"python", "-m", "<推理入口>",

"--text", text, "--voice", voice, "--output", wav_path,

], check=True)

def join(paths, out):

parts, sr = [], None

for p in paths:

data, s = sf.read(p, dtype="float32")

if data.ndim > 1:

data = data.mean(axis=1) # 统一为单声道

sr = s

parts.append(data)

gap = GAP_PARA if p.endswith("_para.wav") else GAP_SENT

parts.append(np.zeros(int(sr * gap), dtype=np.float32))

sf.write(out, np.concatenate(parts), sr)

```

所有块必须采样率一致,否则拼接会出现变速或爆音。采样率不一致时先用 ffmpeg -ar <目标采样率> 统一转一遍再拼。

第 9 步:拼接处的韵律一致性调校

这是效果差异最大的一步,具体做这几件事:

1. 固定音色与语速:全程同一个 voice、同一组 speed/pitch 参数,不要在块之间调。

2. 块长尽量均匀:都在 200–350 字符之间,避免出现一个 30 字符的碎块紧接一个 400 字符的长块——模型对长短输入的韵律处理并不一致。

3. 让每块以句号结尾:如果切出来是逗号结尾,宁可多带一句过去,也别把逗号留在块尾,否则块尾会升调,下一块又从平调起,听感像换人。

4. 段间静音大于句间静音:句间 0.2–0.3 秒、段间 0.5–0.8 秒,接近真实朗读的停顿节奏。

5. 块首块尾去空格与引号:多余标点会被读成额外停顿。

6. 若有随机采样,固定随机种子:让同一批任务可复现。

7. 末尾统一做响度归一化:各块音量本身可能就有几个 dB 的差异,用 ffmpeg 的 loudnorm 滤镜整段过一遍,比逐块处理更稳。

```bash

ffmpeg -i joined.wav -af loudnorm=I=-16:TP=-1.5:LRA=11 -ar 44100 final.wav

```

验证部署是否成功

按顺序跑这三条:

```bash

1. GPU 与基础依赖

python -c "import torch, soundfile; print(torch.cuda.is_available(), soundfile.__version__)"

2. 音频参数

ffprobe -v error -show_entries stream=sample_rate,channels,duration \

-of default=nw=1 final.wav

```

预期:第一条输出 True 和一个版本号;第二条输出采样率(以模型输出为准,常见 22050 或 24000)、声道数 1、时长与你文本量大致匹配。再听一遍成品,检查三点:块与块之间有没有明显的"重新起头"感、句尾有没有异常的升调、整体音量是否平稳。三项都过,部署就算完成。

常见报错与解决

1. torch.cuda.OutOfMemoryError: CUDA out of memory

→ 原因为单块文本过长或显存被其他进程占用。

→ 解决:把 max_chars 降到 200 以下、开启半精度、关闭占卡进程;仍不够时临时设置分配策略:

```bash

export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True

```

2. KeyError: '<音色名>' 或 Unknown speaker

→ 原因:音色名与配置里的键不一致,或者大小写、连字符写法有差异。

→ 解决:先列出可用音色,照抄输出里的名字:

```bash

python -m <推理入口> --list-voices

```

3. FileNotFoundError: ffmpeg 或 sndfile 相关报错

→ 原因:系统缺少音频处理工具,或装了但不在 PATH 里。

→ 解决:

```bash

sudo apt install -y ffmpeg libsndfile1

which ffmpeg

```

4. torch not compiled with CUDA enabled 或 libcudart.so 找不到

→ 原因:装的 PyTorch 是 CPU 版本,或与驱动的 CUDA 版本不匹配。

→ 解决:回到 PyTorch 官方安装页面,按本机 nvidia-smi 显示的 CUDA 版本重装,装完重新验证 torch.cuda.is_available()。

5. 权重文件只有几百字节,推理时报权重格式错误

→ 原因:仓库用了 Git LFS,但没拉取真实文件。

→ 解决:

```bash

git lfs install && git lfs pull

ls -lh models/ # 确认文件大小正常

```

6. 输出是静音或纯噪声

→ 原因:文本为空、采样率设置与模型输出不符,或输入里混入了不可见字符。

→ 解决:先 cat -A 检查块文本,确认非空且只有可见 ASCII;再用最小输入 "Hello." 复现一次,排除文本因素。

后续维护

  • 锁依赖:首次跑通后立刻 pip freeze > requirements.lock,之后升级先在新 venv 里试,不要直接改生产环境。
  • 备份三类东西:音色筛选表、文本规范化规则(数字/缩写映射)、拼接参数(块长、静音时长、响度目标)。这三样丢了,重跑出来的音频风格会变,返工成本高于模型本身。
  • 日志:每块记录字符数、耗时、是否失败。据此算实时率(生成秒数 / 音频秒数),数值明显上升通常意味着显存吃紧或系统在换页。
  • 断点续跑:批量任务按块编号落盘,已存在的 wav 直接跳过,长任务中断后不用从头再来。
  • 磁盘清理:中间块文件体积不小,成品验收后清理 chunks/ 与 out/,只保留最终 wav 和脚本。
  • 升级策略:模型或依赖升级后,用固定的 3 句测试文本 + 固定的 2 个音色重跑一次 A/B,确认口音和韵律没有明显漂移再全量切换。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。