告别机械音!这款开源 TTS 工具让你的文字"活"起来

0 阅读6分钟

文章结尾写在前面:写这篇文章时,我刚从房间里出来。儿子枕着《揭秘恐龙》睡着了,小手还攥着书页角。我轻轻抽走书,指尖碰到他温热的额头,我想,这一刻,任何人工智能都无法复刻。科技再向前奔跑,我也永远会坐在儿子和女儿的床边,为他们讲那些睡前故事。那些即兴的停顿、模仿兔子的傻气、被孩子打断时的耐心,才是故事真正的灵魂。但SpeakEasy的意义,在于“成全”而非“替代”。它让我能把精力留给更珍贵的部分,或许我可以用它去做一个英语App去读一些英文原著(我测试过,发音还是很标准,至于感情嘛,我也看不懂...),或者还可以做一个唐诗三百首的平板应用。科技的终点,应该是人的内心啊。

每晚睡觉前,我儿子都会缠着我讲故事,有些绘本的故事真的太长了,一本书讲下来都有些口干舌燥。后来我尝试用一些 TTS 工具去代替我,有一次读《小王子》。念到“真正重要的东西,用眼睛是看不见的”时,机械的语调让他抬起头问:“爸爸,小王子怎么什么时候都一个语气啊?”我楞了一下,我想让技术传递情感,却不曾想知道了隔阂。前几日,小米发布了一系列大模型,其中就有一个 TTS 模型:MiMo-V2-TTS。

我在查阅官方文档的时候,发现了这么一句话:“通过 [音频标签] ,你可以对声音进行细粒度控制,精准调节语气、情绪和表达风格——无论是低声耳语、放声大笑,还是带点小情绪的小吐槽,也可以灵活插入呼吸声,停顿,咳嗽等,都能轻松实现。语速同样可以灵活调整,让每句话都有它该有的节奏。”我一下子想起了那个《小王子》的故事,于是决定试一试。

这,就是 SpeakEasy 诞生的起点。

SpeakEasy 是一款基于 Rust 开发的桌面端文本转语音工具,调用小米 TTS API (免费!免费!免费) 实现语音合成,当然它也支持通过修改配置项,调用任意 TTS API。在开发过程中,我只有两个朴素目标:

  • 让语音有细节:停顿、呼吸、语气转折,贴近真人表达
  • 让操作有温度:界面简洁,配置清晰,新手也能三分钟上手

你可以通过文字自然表达情绪,比如:

<style>温柔</style>宝贝,该睡觉啦~
(轻声)晚安……(停顿)做个好梦。

它还支持方言与角色

<style>粤语</style>今日饮茶未呀?
<style>悄悄话</style>(压低声音)这个秘密只告诉你哦

需要说明的是,使用时需自行申请小米TTS API(小米大模型官方地址:platform.xiaomimimo.com/)密钥,国内用户约5分…

项目已经开源,如果你觉得这个工具有用,欢迎 Star ⭐ 支持!

相关链接

以下内容是关于项目的技术栈部分,介绍项目中使用的所有库和框架的全面图谱,方便开发者跟进后续页面中更深层次的技术讨论。该项目是一个精简的桌面应用程序——仅包含 4 个源文件和 14 个依赖项——但每个 crate 都是为解决文本转语音流水线中的特定问题而精心挑选的。

架构概览

项目遵循简洁的分层架构,每个源模块对应单一职责,而依赖 crate 则作为这些层之间的连接纽带。下图从宏观层面展示了模块与外部 crate 之间的关系:

架构刻意保持简单:gui.rs 掌控事件循环并协调所有交互,tts.rs 处理远程 API 调用,player.rs 管理音频播放,config.rs 负责持久化用户设置。各模块之间通过直接函数调用进行通信,没有事件总线或消息代理。

ScreenShot_2026-03-23_222024_692.png

模块依赖图

模块之间的依赖关系严格保持单向性。gui 模块是唯一的协调者——它依赖于其他三个同级模块,但这些模块彼此之间互不依赖。这形成了一个清晰的扇入拓扑结构,其中 main.rs 仅知晓 gui.rs,而 gui.rs 则扇出至其余模块。

这种架构意味着你可以按任意顺序研究 config.rstts.rsplayer.rs——它们彼此完全独立。gui.rs 模块是唯一将所有内容串联起来的文件,因此自然成为最后剖析的模块。

ScreenShot_2026-03-26_154801_209.png

模块职责

每个模块都封装了一个特定的领域关注点,并提供了定义明确的公共 API 面。下表总结了每个模块的内部职责及其向应用其他部分暴露的接口。

模块领域核心 Public 类型核心功能
main.rs应用引导创建窗口,应用主题/字体,启动 TtsApp
config.rs设置管理Config (struct)加载/保存 JSON 配置,合并环境变量,解析系统路径
tts.rsAPI 集成TtsClient, TtsRequest, TtsResponse调用 TTS API,解码 Base64 音频,生成文件名
player.rs音频播放AudioPlayer通过 rodio 播放/停止 WAV 文件
gui.rs用户界面TtsApp (实现 eframe::App)渲染所有 UI,分发异步任务,协调各模块

入口点流程

应用的生命周期始于 main.rs,包含清晰的三阶段初始化序列。首先,定义窗口选项——包括 800×600 像素的初始大小、600×400 像素的最小尺寸,以及中文窗口标题“TTS语音生成工具”(main.rs)。其次,应用自定义的浅色主题,以干净明亮的配色方案覆盖 egui 默认的深色样式 (main.rs)。第三,探测并加载中文字体(微软雅黑、黑体或宋体),以确保 CJK 字符能够正确渲染 (main.rs)。实际的应用实例 gui::TtsApp::new(cc) 在最后被构建,接收 egui 的创建上下文。

依赖概览

项目的 Cargo.toml 声明了 12 个按功能分类的外部依赖。其 release 配置文件针对生产构建进行了调优,启用了优化等级 2 以及链接时优化(LTO)。

分类Crate版本项目中的作用
GUI 框架eframe / egui0.27即时模式 GUI 渲染与窗口管理
HTTP 客户端reqwest0.11向 TTS API 端点发送 POST 请求
异步运行时tokio1用于 API 调用和文件 I/O 的多线程异步运行时
序列化serde / serde_json1用于 API 负载和配置文件的 JSON 序列化
环境变量dotenv0.15加载 .env 文件以配置 API 密钥
时间处理chrono0.4生成带时间戳的输出文件名
音频处理rodio0.17解码并播放 WAV 音频文件
错误处理anyhow1具备上下文信息的便捷错误传播机制
系统路径dirs5解析特定平台的配置与输出目录
文件对话框rfd0.13用于选择输出目录的原生文件夹选择器
Shell 打开open5在系统文件管理器中打开输出文件夹
编码解码base640.22解码 API 响应中的 Base64 编码音频数据