使用说明
Kokoro TTS Kotlin
一个纯JVM文本到语音服务器,由 科科罗-82M 神经TTS模型。在JVM上本机运行ONNX推理——没有Python,没有外部服务。通过Ktor和MCP端点为人工智能辅助集成提供REST API服务。
支持单语音合成、具有自然轮距的多语音对话、语音混合、外来词的内联音素注释以及24 kHz的WAV/MP3输出。
构建这个项目的整个过程——从模型研究和G2P工程到干净的架构、部署和性能调优——在 如何构建听起来不错的自托管TTS.
安装
先决条件
- JDK 25+ --下载自 甲骨文 或通过安装 SDKMAN!:
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],每圈文字<=5000个字符,至少一圈。
其他终点
| 端点 | 描述 |
|---|---|
GET /health |
健康检查(退货 OK) |
GET /v1/voices |
列出带有ID和语言的可用语音 |
/mcp |
用于AI辅助集成(SSE传输)的MCP端点 |
/swagger |
交互式API文档 |
错误
| 状态 | 条件 |
|---|---|
| 400 | 文本太长,速度超出范围,对话为空,JSON格式错误 |
| 404 | 找不到语音 |
| 500 | 推断或存储故障 |
MCP集成
Ktor服务器和Lambda都公开了MCP(模型上下文协议)端点,使Claude Desktop等AI助手能够将语音合成为工具调用。
工具:
list_voices--返回所有语音ID和语言synthesize_speech--具有文本、语音、速度、格式参数的单语音合成synthesize_dialogue--每回合有不同声音的多回合对话
工具描述指导LLM将外来专有名词包装在 (word)[IPA] 正确发音的注释。
MCP检验员测试: 连接到 http://localhost:8080/mcp (苏格兰和南方能源公司运输)。
克劳德桌面版 (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"kokoro-tts": {
"command": "npx",
"args": ["mcp-remote", "http://localhost:8080/mcp"]
}
}
}
建筑
五个Gradle模块的简洁架构:
- 领域 --纯值类型(
VoiceId,SpeechRate,AudioFormat,SynthesisException),零依赖性 - 核心 --端口接口(
PhonemeGenerator,InferenceEngine,AudioEncoder,VoiceRepository,AudioStorage)DTO、用例、TTS服务编排 - 基础设施 --适配器:ONNX推理、POS感知G2P(OpenNLP+misaki字典)、WAV/MP3编码、本地/S3存储、MCP服务器工厂
- 应用 --Ktor HTTP层与Koin DI组合
- λ --带有单例冷启动初始化的AWS Lambda处理程序
TTS管道
Text --> 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音素 美咲的逻辑。四个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映射到过去时发音)。
双通道管道:
- 第一次通过 --POS标记所有代币 Apache OpenNLP (感知器模型、Penn Treebank标签、保留原始大小写以提高标签准确性),通过具有形态词干和字母规则回退的POS感知词典查找来解析音素
- 第二遍 --反向扫描音素进行计算
futureVowel上下文,应用虚词覆盖(“the”→ði/ðə“to”→tʊ/tə/tu),重新查找带有重音的句子末尾单词None-关键变体
单词分辨率 (按优先顺序):
- 内联音素注释 —
(word)[IPA]语法绕过了整个G2P管道 - 收缩扩张 --例如,在音素化之前,“I'm”被扩展为“I'am”
- 上下文相关虚词 --“the”使用
ði元音之前/ðə辅音之前;“to”使用tʊ元音之前/tə辅音之前/tu句末 - 缩写 --包含2+个大写字母的单词逐字母拼写
- 数量扩展 --整数、小数、前导零序列扩展为单词
- POS感知字典查找 --基于父标签规范化(VBD)的POS标签选择正确的变体→动词,NN→名词,JJ→ADJ,RB→ADV)和句子最终重音形式
None钥匙 - 形态堵塞 --复数、过去时、进行式、状语、agent、带有美式英语T-flaping的私有后缀(词干结尾为“T”)→ 'ɾ在-ed/-ing元音之前)
- 复合分词 --尝试所有拆分位置(最少3个字符),降低第二部分的压力
- 字母到音素回退 --约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 了解详细的编码约定、架构规则和设计决策。
技术栈
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编码器)。
