Pocket TTS有声书生成实战:长文本切分、48kHz升采样与错误检测
【免费下载链接】pocket-ttsA TTS that fits in your CPU (and pocket)项目地址: https://gitcode.com/GitHub_Trending/po/pocket-tts
Pocket TTS 是一款专为 CPU 打造的轻量级文字转语音(TTS)工具,仅 1 亿参数即可在普通电脑上以约 6 倍实时速度生成自然语音。本文将带你用 Pocket TTS 完成有声书生成全流程:理解长文本如何被自动切分成短句块、如何将 24kHz 输出升采样到 48kHz 高保真采样率,以及如何检测与处理"吞字、重复"等合成错误。
一、为什么 Pocket TTS 适合做有声书?📚
多数 TTS 模型需要 GPU 或云端 API,而 Pocket TTS 的核心卖点恰恰相反:
| 特性 | 说明 |
|---|---|
| 纯 CPU 运行 | 无需显卡,pip install pocket-tts即用 |
| 超长文本支持 | 可处理任意长度的输入,自动切分为短句块 |
| 低延迟 | 约 200ms 拿到第一块音频,边生成边写文件 |
| 多语言 | 英文、法文、德文、葡文、意大利文、西班牙文 |
它对有声书场景尤其友好:模型逐句生成音频并流式写入 WAV,一本几十万字的小说也能稳定跑完。
二、长文本切分:句子边界 + 50 Token 上限
把整本小说一次性喂给模型并不可行——Pocket TTS 的训练数据是单句,长输入必须拆开。切分逻辑集中在 text_chunking.py 中的split_into_best_sentences(),流程分三步:
- 按句号/问号/感叹号切句:把文本切到 SentencePiece token 层面找句子边界,并且会智能跳过小数点——
3.14这种数字中间不会被误切开; - 合并成块:相邻短句按顺序合并,直到累计 token 数接近上限(默认
MAX_TOKEN_PER_CHUNK = 50,定义在 default_parameters.py); - 超长句降级切分:单句超过 50 token 时,退而求其次按逗号、分号、冒号再拆一次,防止整句被跳过。
在切块之前,prepare_text_prompt()还会做几件"文本整形"的小事:
- 换行符统一替换为空格,分号替换为逗号;
- 首字母强制大写;
- 末尾没有
.!…?时自动补句号——因为缺少句末标点会导致最后一个词发音错误或重复; - 少于 5 个词极短的输入会在前面补空格凑 token 数,避免模型在小输入上表现不稳定。
每个文本块生成后,若仍超过 token 上限,代码会打印如下警告(见 text_chunking.py):
Chunk has 62 tokens (max 50), generation may skip words: '...'
这条警告就是有声书批量生产中最实用的"错误信号"——出现它的段落建议单独重跑。
三、生成有声书:CLI 与 Python API 两种方式
方式 1:CLI 一键生成
pocket-tts generate \ --text "$(cat chapter1.txt)" \ --voice alba \ --output-path ./chapter1.wav输出为标准 WAV:24kHz、单声道、16-bit PCM(参数详见 generate.md)。想换语言用--language,想加速可加--quantize做 int8 量化。
方式 2:Python API 按章节批量生成
from pocket_tts import TTSModel import scipy.io.wavfile model = TTSModel.load_model() voice_state = model.get_state_for_audio_prompt("alba") # 只加载一次,多章节复用 for i, chapter in enumerate(chapters): audio = model.generate_audio(voice_state, chapter) scipy.io.wavfile.write(f"chapter_{i}.wav", model.sample_rate, audio.numpy())关键实践:模型和声音状态只加载一次。load_model()与get_state_for_audio_prompt()是慢操作,按章节循环时复用它们可省去大量重复初始化(API 详情见 python-api.md、tts_model.py)。
四、48kHz 升采样:从 24kHz 到广播级音质
Pocket TTS 原生输出是 24kHz,而不少音频平台和母带交付要求 48kHz。仓库内置的 audio_utils.py 提供了现成的重采样函数convert_audio(),它基于 scipy 的resample_poly按最大公约数计算升降采样比(24k→48k 即 2 倍整数倍,无精度损失),代码库内部处理声音提示音频时也是调用它:
from pocket_tts.data.audio_utils import convert_audio import torch, scipy.io.wavfile wav, sr = scipy.io.wavfile.read("chapter1.wav") audio = torch.from_numpy(wav.astype("float32") / 32768.0).unsqueeze(0) audio_48k = convert_audio(audio, from_rate=sr, to_rate=48000, to_channels=1) scipy.io.wavfile.write("chapter1_48k.wav", 48000, (audio_48k.squeeze(0).numpy() * 32767).astype("int16"))💡 升采样只是"补齐采样点",并不能创造 24kHz 以上不存在的高频信息。对有声书而言,它主要保证与平台交付规范一致、方便后续混音与响度标准化。
五、错误检测:别让"吞字"悄悄混进成品
批量合成几百段音频,最怕的是模型偶尔"吞词"或"复读"。以下是三条可落地的检测与兜底策略:
1. 用切分警告做第一道过滤。如上节所述,超 50 token 的块会打印警告。写脚本时捕获日志(logging),凡含may skip words的章节直接标记重跑,无需人工收听。
2. 控制每个块的"呼吸空间"。生成参数--eos-threshold(默认 -4.0)和--frames-after-eos(EOS 后保留帧数,每帧 80ms)影响句子收尾质量。收尾太紧容易吞掉最后一个词,适当放宽frames-after-eos可降低风险(参数说明见 generate.md)。
3. 逐段落生成 + 时长校验。按段落而非整章生成,每段落存独立文件;再粗略校验"文本字数 / 音频时长"是否在合理区间(英文约 2~3 词/秒)。时长显著偏短的段落大概率漏读,重新生成为止。这也是社区有声书工具(见 README.md 中 tts-audiobook-tool 项目)"自动错误检测"的通用思路。
# 重试式生成:同一段落最多重试 3 次 for attempt in range(3): audio = model.generate_audio(voice_state, paragraph) if audio.shape[-1] / model.sample_rate > expected_min_seconds: break六、常见坑速查表 🧭
| 现象 | 原因 | 处理 |
|---|---|---|
| 末尾单词重复/发音怪 | 文本块缺少句末标点 | 确保每段以.?!结尾(或交给自动补全) |
| 长句中间跳字 | 单句 token 超 50 上限 | 手动断句,或依赖逗号降级切分 |
| 生成慢到不可用 | Linux 上误装了 CUDA 版 PyTorch | 从 PyTorch CPU 源安装(见 README.md CPU-only 一节) |
| 内存占用高 | 长声音提示未截断 | 加载声音状态时开启truncate=True(截断到 30 秒,见 tts_model.py) |
结语
Pocket TTS 用"句子级切分 + 流式生成"的思路,把原本依赖 GPU 的 TTS 有声书生产搬到了普通 CPU 上。掌握本文三个要点——长文本切分规则、24kHz→48kHz 升采样、吞字错误检测——你就能用几十行脚本稳定产出整本书的音频。更多参数与用法可查阅 docs/ 目录下的完整文档与 quantization.md 量化指南。
【免费下载链接】pocket-ttsA TTS that fits in your CPU (and pocket)项目地址: https://gitcode.com/GitHub_Trending/po/pocket-tts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考