Kokoro TTS Kotlin
      
一个纯JVM文本到语音服务器,由 科科罗-82M 神经TTS模型。在JVM上本机运行ONNX推理——没有Python,没有外部服务。通过Ktor和MCP端点为人工智能辅助集成提供REST API服务。
支持单语音合成、具有自然轮距的多语音对话、语音混合、外来词的内联音素注释以及24 kHz的WAV/MP3输出。
构建这个项目的整个过程——从模型研究和G2P工程到干净的架构、部署和性能调优——在 如何构建听起来不错的自托管TTS.
安装
先决条件
sdk install java 25-open- 卷曲 --数据下载脚本所需(预装在macOS/Linux上)
- AWS凭据 (可选)——仅S3存储模式需要;本地模式不需要AWS设置
克隆和设置
git clone https://github.com/alexsobolev/kokoro-tts-kotlin.git
cd kokoro-tts-kotlin下载模型文件
TTS管道需要模型权重、语音嵌入和发音词典(总共约400 MB)。该脚本跳过已存在的文件。
./scripts/download-data.sh此下载到 data/ 目录:
| 文件 | 大小 | 来源 | 描述 |
|---|---|---|---|
kokoro-v1.0.int8.onnx | 92.3毫巴 | kokoro onnx | 量化ONNX TTS模型 |
voices-v1.0.bin | 28.2毫巴 | kokoro onnx | 语音风格嵌入 |
config.json | 2.3 KB | 科科罗-82M | 标记器词汇 |
us_gold.json | 3.0 MB | 美咲 | 美式英语黄金发音格言 |
us_silver.json | 3.0 MB | 美咲 | 美式英语银色发音格言 |
gb_gold.json | 2.8 MB | 美咲 | GB英语黄金发音格言 |
gb_silver.json | 3.6 MB | 美咲 | GB英语银色发音词典 |
en-pos-perceptron.bin | 3.9毫巴 | Apache OpenNLP | OpenNLP POS标签模型 |
lexicon_fixes.json | 0.1 KB | 本地 | 自定义发音覆盖 |
配置环境
默认情况下,服务器使用 本地存储 --音频文件被写入 output/ 目录,并通过HTTP提供服务。无需AWS凭据。
对于S3存储,设置:
export STORAGE_MODE=s3
export AWS_REGION=eu-central-1
export S3_BUCKET=my-tts-bucket看 配置 对于所有可用设置。
构建和验证
./gradlew build # Compile, lint, static analysis, and tests快速开始
下载模型和词典文件(需要一次;请参阅 下载模型文件):
./scripts/download-data.sh然后运行服务器:
./gradlew :app:run服务器在端口上启动 8080 使用本地文件存储(不需要AWS)。音频文件保存到 output/ 并在 http://localhost:8080/audio/....Swagger用户界面位于 /swaggerOpenAPI规范 /openapi.
API
合成语音
POST /v1/tts单一声音:
{
"turns": [
{ "voice": "af_heart", "text": "Hello, world!" }
],
"speed": 1.0,
"format": "wav"
}多语音对话 (与随机250-500ms静默间隙连接):
{
"turns": [
{ "voice": "af_heart", "text": "How are you today?" },
{ "voice": "am_adam", "text": "I am doing great, thanks for asking!" }
],
"speed": 1.2,
"format": "mp3"
}语音混合 (样式嵌入的加权平均值,权重之和必须为1.0):
{
"turns": [
{ "voice": "af_heart:0.6+af_bella:0.4", "text": "A blended voice." }
]
}内联音素注释 对于外来词和专有名词:
{
"turns": [
{ "voice": "af_heart", "text": "We visited (Machu Picchu)[mˈɑːtʃuː pˈiːtʃuː] in (Peru)[pəɹˈuː]." }
]
}响应(本地模式):
{
"url": "http://localhost:8080/audio/af_heart/uuid.wav",
"key": "af_heart/uuid.wav",
"expiresInSeconds": 0,
"sizeBytes": 48044,
"format": "wav",
"voice": "af_heart"
}默认值: speed = 1.0, format =“wav”, voice =“af_heart”。
限制: 速度为\[0.5,2.0\],每圈文字\ EnglishPhonemeGenerator --> KokoroTokenizer --> OnnxKokoroEngine --> SentencePostProcessor --> LocalAudioEncoder --> AudioStorage (POS-aware G2P) (IPA -> tokens) (ONNX @ 24kHz) (volume envelopes) (WAV/MP3) (local disk or S3)
### G2P(从字素到音素)
`EnglishPhonemeGenerator` 使用POS感知混合方法匹配将英语文本转换为IPA音素 [美咲](https://github.com/hexgrad/misaki)的逻辑。四个misaki JSON字典在启动时合并为一个(US gold>US silver>GB gold>GB silver) `PosAwareLexicon` 保留每个POS的发音变体(例如,“live”作为形容词 `lˈIv` vs动词 `lˈɪv`“record”作为名词 `ɹˈɛkəɹd` vs动词 `ɹəkˈɔɹd`).A. `lexicon_fixes.json` 更正文件以最高优先级深度合并,修复了3个上游misaki VBP错误(读取、重读、伤口将现在时态VBP映射到过去时发音)。
**双通道管道:**
1. **第一次通过** --POS标记所有代币 [Apache OpenNLP](https://opennlp.apache.org/) (感知器模型、Penn Treebank标签、保留原始大小写以提高标签准确性),通过具有形态词干和字母规则回退的POS感知词典查找来解析音素
1. **第二遍** --反向扫描音素进行计算 `futureVowel` 上下文,应用虚词覆盖(“the”→ `ði`/`ðə`“to”→ `tʊ`/`tə`/`tu`),重新查找带有重音的句子末尾单词 `None`-关键变体
**单词分辨率** (按优先顺序):
1. **内联音素注释** — `(word)[IPA]` 语法绕过了整个G2P管道
1. **收缩扩张** --例如,在音素化之前,“I'm”被扩展为“I'am”
1. **上下文相关虚词** --“the”使用 `ði` 元音之前/ `ðə` 辅音之前;“to”使用 `tʊ` 元音之前/ `tə` 辅音之前/ `tu` 句末
1. **缩写** --包含2+个大写字母的单词逐字母拼写
1. **数量扩展** --整数、小数、前导零序列扩展为单词
1. **POS感知字典查找** --基于父标签规范化(VBD)的POS标签选择正确的变体→动词,NN→名词,JJ→ADJ,RB→ADV)和句子最终重音形式 `None` 钥匙
1. **形态堵塞** --复数、过去时、进行式、状语、agent、带有美式英语T-flaping的私有后缀(词干结尾为“T”)→ 'ɾ在-ed/-ing元音之前)
1. **复合分词** --尝试所有拆分位置(最少3个字符),降低第二部分的压力
1. **字母到音素回退** --约100个英语字形模式贪婪地与上下文敏感的元音规则相匹配
### 语调后处理
Kokoro模型并不能通过标点符号很好地区分语调。 `TtsService` 和 `SentencePostProcessor` 补偿:
- **提问(`?`)** --0.92倍速度;最后600ms的音量上升斜坡(1.0->1.15x,二次)。在最后一个子句边界处拆分的多子句问题
- **感叹词(`!`)** --前400ms增益提升(1.20x->1.0,线性衰减)
- **声明** --未修改
RMS窗口语音边界检测可确保音量效果目标语音内容,而不是模型生成的静音。
### 语音混合
混合语音(例如。, `af_heart:0.6+af_bella:0.4`)通过对256维样式嵌入进行加权平均来创建。混合后,结果向量被L2重整化为输入范数的加权平均值——否则,混合向量的幅度较小,产生的音频质量下降。
### ONNX推断
- 第一次调用时加载延迟会话,而不是在启动时加载
- 音素序列截断为510个令牌(模型的512个上下文窗口减去BOS/EOS)
- 每段10ms淡入/淡出,消除边界处的点击伪影
- 所有可用的CPU线程通过 `setIntraOpNumThreads()`
## 部署
### Docker(Ktor服务器)
docker build -t kokoro-tts . docker run -p 8080:8080 kokoro-tts # local storage (default) docker run -p 8080:8080 \ -e STORAGE_MODE=s3 \ -e AWS_REGION=eu-central-1 \ -e S3_BUCKET=my-tts-bucket \ kokoro-tts # S3 storage
非root用户,3GB堆, `ExitOnOutOfMemoryError`。使用Gradle依赖缓存的多阶段构建。
### 拉姆达
docker build -f Dockerfile.lambda -t kokoro-tts-lambda .
自定义JDK 25运行时(超出AWS管理的运行时),ONNX模型的8 GB堆。Koin DI在每个容器冷启动时初始化一次。自动检测和解码来自Lambda函数URL的base64请求体。
## 配置
`app/src/main/resources/application.yaml`:
tts: tokenizer: configPath: "data/config.json" voices: path: "data/voices-v1.0.bin" phonemizer: goldDictPath: "data/us_gold.json" silverDictPath: "data/us_silver.json" gbGoldDictPath: "data/gb_gold.json" gbSilverDictPath: "data/gb_silver.json" fixesDictPath: "data/lexicon_fixes.json" pos: modelPath: "data/en-pos-perceptron.bin" model: onnxPath: "data/kokoro-v1.0.int8.onnx" aws: region: "$AWS_REGION:" s3Bucket: "$S3_BUCKET:" storage: mode: "$STORAGE_MODE:local" prefix: "$STORAGE_PREFIX:tts-audio" localOutputDir: "$LOCAL_OUTPUT_DIR:output" baseUrl: "$BASE_URL:http://localhost:8080"
所有设置都支持使用Ktor的环境变量覆盖 `$ENV_VAR:default` 语法。Lambda处理程序直接从环境变量中读取相同的设置。
|变量|默认值|描述|
|----------|---------|-------------|
| `STORAGE_MODE` | `local` |存储后端: `local` (磁盘+HTTP)或 `s3` |
| `LOCAL_OUTPUT_DIR` | `output` |本地音频文件目录|
| `BASE_URL` | `http://localhost:8080` |本地音频下载链接的公共基础URL|
| `AWS_REGION` |--|AWS区域(需要 `s3` 模式)|
| `S3_BUCKET` |--|S3铲斗(需要 `s3` 模式)|
| `STORAGE_PREFIX` | `tts-audio` |S3对象密钥前缀|
## 建筑
./gradlew build # Compile + ktlint + detekt + tests ./gradlew :app:run # Dev server on port 8080 ./gradlew buildFatJar # Fat JAR at app/build/libs/app-all.jar ./gradlew ktlintFormat # Auto-format code ./gradlew koverHtmlReport # Merged code coverage report → build/reports/kover/html/ ./gradlew koverXmlReport # Merged XML coverage report (for CI)
### 代码质量
构建运行 **ktlint** (格式化), **检测** (静态分析),所有测试都作为一个门进行。 **Kover** 强制执行最低限度 **85%** 所有五个模块的行覆盖率,并在根级别进行合并报告。所有测试都遵循给定的when-then模式 `// given`, `// when`, `// then` 部分评论。
看 [指南.md](GUIDELINES.md) 了解详细的编码约定、架构规则和设计决策。
## 技术栈
Kotlin 2.3.0、JDK 25、Ktor 3.4.0、Koin 4.1.1、ONNX Runtime 1.23.2、Apache OpenNLP 2.5.3、kotlinx.serialization 1.8.1、AWS Kotlin SDK 1.6.12、MCP SDK 0.8.4、jump3r(LAME MP3编码器)。