tts-server-android 自定义TTS插件开发指南:如何从零写出第一个可用的 Android TTS 插件
tts-server-android 自定义TTS插件开发指南如何从零写出第一个可用的 Android TTS 插件【免费下载链接】tts-server-android这是一个Android系统TTS应用内置微软演示接口可自定义HTTP请求可导入其他本地TTS引擎以及根据中文双引号的简单旁白/对话识别朗读 还有自动重试备用配置文本替换等更多功能。项目地址: https://gitcode.com/GitHub_Trending/tt/tts-server-androidtts-server-android 是一款开源的 Android 系统级 TTS 应用内置微软演示接口并支持插件化扩展——你只需用 JavaScript 就能把自己的 TTS 服务做成自定义TTS插件挂进系统朗读链路。这篇文章面向想接入自有 TTS 接口、但没写过 JS 插件的开发者带你从零写出第一个能用的插件并覆盖 SSML 情感合成、多语言混排这些进阶玩法。一、内置 TTS 不够用的几个痛点插件来补位你可能已经碰到过这些情况内置音色太少系统自带那几个读完就腻想换也没得选情绪不可调广告腔、耳语、新闻腔统统切不了语气永远一个调中英混排读串味我明天有 standup 会议里的英文经常被按中文语感读接不了自己的服务你部署的 TTS 接口在应用里没有入口tts-server-android 的解法很直接把合成逻辑交给一段 JavaScript 脚本由应用内嵌的Rhino JS 引擎一个跑在 Android 上的 JavaScript 解释器直接执行——你不用写一行 Kotlin也不用懂 Android 原生开发。整个插件架构分三层应用层界面、配置管理、分组与备用机制引擎层Rhino JS 引擎负责解析和运行插件脚本插件层你的 JavaScript 文件负责真正的合成请求插件本质上是文本文件按插件 ID 存放在缓存目录/Android/data/com.github.jing332.tts_server_android/caches/插件ID在应用里随时增、删、改都不需要重新编译这就是插件热插拔新插件保存完界面立刻就能选到它。二、写出第一个 PluginJS最小可运行插件先交代环境不需要专门配 Android 开发环境克隆源码主要是查模板和示例git clone https://gitcode.com/GitHub_Trending/tt/tts-server-android插件脚本本身在应用内置的编辑器里粘贴保存即可。动手前先看官方插件模板 app/src/main/assets/help/js/tts.md它列出了全部回调。一个插件脚本由两个顶层对象组成PluginJS管合成逻辑EditorJS管界面和数据回调。下面这段是最小可运行骨架——请求你的 HTTP 接口返回音频流let PluginJS { name: 我的TTS, // 应用里显示的名称 id: com.example.mytts, // 唯一ID同时作为文件目录名 author: you, version: 1, // 整数用于更新检测 // 核心入口输入文本返回音频 getAudio: function (text, locale, voice, speed, volume, pitch) { let resp ttsrv.httpPost(https://your-tts-api/v1/synthesize, { text: text, voice: voice, format: mp3 }, { Authorization: Bearer YOUR_KEY }) if (resp.code() ! 200) { throw 合成失败: HTTP- resp.code() } return resp.body().byteStream() // 音频输入流 } } let EditorJS { getLocales: function () { return [zh-CN, en-US] }, getVoices: function (locale) { // key 会作为 getAudio 的 voice 参数value 是显示名 return { demo: 演示音色 } } }getAudio的返回值比较宽容音频输入流、字节数组ByteArray/Uint8Array、ArrayBuffer或者直接返回一个 http(s) 音频地址都行。长文本场景建议改用getAudioV2(request, callback)request里带text / voice / rate / volume / pitch通过callback.write(bytes)分块写出、callback.close()收尾、callback.error(msg)报错实现边合成边播放。三个音频参数怎么换算应用传给getAudio的音频参数统一是 0~100 的整数进插件后按需换算成百分比参数传入范围换算后含义speed 语速0~100(speed × 2) − 100即 -100~100负值放慢、正值加快单位 %volume 音量0~100原样使用0 静音100 最大pitch 音调0~100pitch − 50即 -50~50负值降调、正值升调单位 %换算写法可以直接参考仓库里的示例插件 app/src/main/assets/defaultData/plugin-azure.js而超时、重试、流式播放这些合成侧的配置集中在 lib-tts/src/main/java/com/github/jing332/tts/SynthesizerConfig.kt 里定义。三、两个值得深挖的高级能力SSML 情感合成让同一音色换语气使用场景同一个音色读新闻要严肃、念广告要上扬、耳语要压低——纯文本接口做不到得上SSMLSpeech Synthesis Markup Language语音合成标记语言一种描述怎么读的 XML 标记。实现要点请求体从纯文本换成 SSML 片段mstts:express-as指定风格style、强度styledegree、角色roleprosody负责rate / pitch / volume用户在界面上的选择存进ttsrv.tts.datagetAudio时读出来拼进请求文本必须先做 XML 转义否则特殊字符会打断标记结构下面这段代码演示了读用户配置 → 拼 SSML → 发给服务的关键链路getAudio: function (text, locale, voice, speed, volume, pitch) { let rate (speed * 2) - 100 let style ttsrv.tts.data[style] || general let role ttsrv.tts.data[role] || default let ssml speak xmlnshttp://www.w3.org/2001/10/synthesis xmlns:msttshttps://www.w3.org/2001/mstts xml:lang${locale} voice name${voice} mstts:express-as style${style} styledegree1.0 role${role} prosody rate${rate}% volume${volume}${escapeXml(text)}/prosody /mstts:express-as /voice /speak return postToService(ssml) } // 特殊字符转义防止文本破坏 SSML 结构 function escapeXml(s) { return s.replace(//g, amp;).replace(//g, lt;) .replace(//g, gt;).replace(//g, quot;) }多语言混合发音中英混排不读串使用场景中英混排文本默认按主语言读音英文词容易读错。实现要点两种思路——把文本按语言切段、逐段合成再拼接或者更省事若目标音色支持二级语言Secondary Locale直接给片段套一层lang强制发音语言。音色支持哪些二级语言看它的SecondaryLocaleList示例插件的onVoiceChanged就是据此刷新语言技能下拉框的。强制指定发音语言的关键写法只有两行let body lang xml:langen-US${escapeXml(mixedText)}/lang let ssml speak xml:langzh-CNvoice name${voice}${body}/voice/speak四、EditorJS 配置界面让插件在应用里看得见摸得着插件写完用户还需要能配置它这部分全靠EditorJS对象。它管两类事数据加载与界面控件onLoadData跑在 IO 线程适合拉网络数据并落盘缓存示例插件就是把voices.json拉到本地下次直接读onLoadUI(ctx, linerLayout)直接往布局里加 Android 控件——下拉框用JSpinner、滑杆用JSeekBar、文本框用普通 EditText保存逻辑控件回调里把值写进ttsrv.tts.datagetAudio再读出来形成界面 → 数据 → 合成的闭环声明式变量密钥、区域这类必填项用PluginJS.vars声明一次用户在更多选项 → 设置变量里填代码从ttsrv.userVars读取let PluginJS { vars: { key: { label: 密钥 Key }, region: { label: 区域 Region, hint: 为空时默认 eastus } }, onLoad: function () { if (!(ttsrv.userVars[key] ).trim()) { throw 请先在设置变量中填写密钥 } } }控件侧同样简单一个下拉框加一行保存逻辑就够了onLoadUI: function (ctx, linerLayout) { let styleSpinner JSpinner(ctx, 风格) styleSpinner.setOnItemSelected((sp, pos, item) { ttsrv.tts.data[style] item.value // 写回数据getAudio 可读取 }) linerLayout.addView(styleSpinner) }配置项常见三种形态文本输入text、下拉选择select对应 JSpinner、滑杆slider对应 JSeekBar。在应用里添加你的插件入口如下五、TTS 插件上线前自查清单发布前逐项过一遍每条都是一行代码的事日志关键路径打console.log运行日志在 App 底部日志页翻 →console.log(getAudio, text)错误处理非 200 必须抛错并带状态码别静默吞掉 →if (resp.code() ! 200) throw 合成失败: resp.code()缓存相同入参的结果存起来设容量上限防止无限膨胀 →if (audioCache.has(key)) return audioCache.get(key)流式长文本改用getAudioV2分块写出边合成边播 →callback.write(chunk); callback.close()资源释放合成停止时在onStop清缓存、断连接 →onStop: function () { audioCache.clear() }耗时定位合成慢时用console.time(tts)/console.timeEnd(tts)掐表写完不必装进系统再验证——应用的网页页签可以直接选引擎、填测试文本、调语速试听六、插件还能玩出什么花样离线端侧模型集成 WebAssembly 或端侧 AI 语音合成彻底摆脱网络依赖实时变声在通话链路里做实时语音转换给语音加趣味效果多模态对话接上语音识别ASR做出说完就答的对话循环有声书流水线自动分析小说文本按情节标注情感风格批量朗读无障碍增强为视障用户做场景化语音提示按情境切换语气风格迁移把普通播报转成特定角色音色打造虚拟主播从第一行PluginJS到上线自查一个自定义TTS插件的开发闭环其实不长getAudio跑通合成、EditorJS搭好配置、清单过完就能用。核心资源都在这几处插件模板app/src/main/assets/help/js/tts.md示例插件app/src/main/assets/defaultData/plugin-azure.js合成器配置源码lib-tts/src/main/java/com/github/jing332/tts/SynthesizerConfig.kt运行时 API 文档http / websocket / file 等app/src/main/assets/help/js/遇到问题可去项目的 Issue 与讨论区发起话题下一个音色由你来写。【免费下载链接】tts-server-android这是一个Android系统TTS应用内置微软演示接口可自定义HTTP请求可导入其他本地TTS引擎以及根据中文双引号的简单旁白/对话识别朗读 还有自动重试备用配置文本替换等更多功能。项目地址: https://gitcode.com/GitHub_Trending/tt/tts-server-android创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

【Nginx】Nginx 代理 WebSocket 连接:从握手升级到长连接保活的完整指南

【Nginx】Nginx 代理 WebSocket 连接:从握手升级到长连接保活的完整指南

Nginx 代理 WebSocket 连接:从握手升级到长连接保活的完整指南 在实时通信场景中,WebSocket 已成为标配——无论是在线客服、协同编辑,还是金融行情推送。但许多团队在将 WebSocket 接入 Nginx 时,常遇到 “连接秒断”“400 Bad Request”“502 Gateway Error” 等问题。 …

2026/10/17 6:03:33 阅读更多 →
提示词工程实战指南:从原理到调优,让大模型输出更稳定

提示词工程实战指南:从原理到调优,让大模型输出更稳定

先说个结论:提示词工程说白了,就是一门研究“怎么跟大模型把话说清楚”的学问。很多刚接触AI大模型LLM的朋友,总觉得会写提示词的人是在念什么咒语,其实真不是。模型再聪明,它也没法读心,你给它一段含含糊糊…

2026/10/17 6:03:33 阅读更多 →
STM32固件库变量类型详解:从uint32_t到__IO,避开嵌入式C的隐含陷阱

STM32固件库变量类型详解:从uint32_t到__IO,避开嵌入式C的隐含陷阱

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 6:02:33 阅读更多 →

最新新闻

Windows虚拟化排查指南:从BIOS到Hyper-V的彻底解决

Windows虚拟化排查指南:从BIOS到Hyper-V的彻底解决

这个标题一看就是被模拟器、虚拟机折腾过的人写出来的。说实话,Windows上这颗“虚拟化”的雷,我踩过不下几十次,帮身边朋友排查也排了无数轮。明明主板的开关开了,BIOS里也看到了,系统里却还是提示“CPU 虚拟化已禁用”…

2026/10/17 7:08:04 阅读更多 →
智能体互联协议选型指南:MCP、A2A、ANP对比与避坑实践

智能体互联协议选型指南:MCP、A2A、ANP对比与避坑实践

简介:围绕智能体通信协议MCP、A2A、ANP的演进背景与设计差异,这份PDF面向AI研究者、智能体开发者及关注智能体互联网趋势的企业管理者,系统梳理三类协议的定位、适用场景与选型思路。内容以ANP为主线展开,说明其作为面向智能体设计…

2026/10/17 7:08:04 阅读更多 →
174、让Agent看懂图表:视觉推理

174、让Agent看懂图表:视觉推理

这一篇笔记,得从上周调的一个生产问题说起。客户那边有个Agent,任务是盯着一堆行业报告里的柱状图,自动提取某个季度的营收数据。日志里看,模型明明把图“看”了,也输出了数字,可跟真实报表一对,差了整整一个数量级。最诡异的是,它给的结论跟图例里的单位完全对不上——…

2026/10/17 7:08:04 阅读更多 →
无人驾驶轨迹跟踪与模型预测控制:配套程序整理及调参经验

无人驾驶轨迹跟踪与模型预测控制:配套程序整理及调参经验

前几天我把手头这套无人驾驶车辆轨迹跟踪与模型预测控制第二版配套程序完整整理了一遍。整理之前我以为只是把文件分类,真正动手才发现,这个“配套程序”牵扯的东西远比想象中多:车辆模型用运动学还是动力学、预测时域取多少步、约束怎么设、…

2026/10/17 7:08:04 阅读更多 →
175、语音Agent:STTTTS与对话

175、语音Agent:STTTTS与对话

昨晚线上一个语音Agent在嘈杂环境下把“播放周杰伦的晴天”识别成了“播放周杰伦的情天”,用户重复了三遍,最后气的直接关了。我盯着日志里那句带“情天”的VAD切片,愣了半天。语音Agent这个东西,文档里写的都是“ASR识别-语义理解-TTS合成”的大闭环,但真正上线你会发现,…

2026/10/17 7:08:04 阅读更多 →
零基础网络安全攻防入门:从攻击者视角构建防御体系

零基础网络安全攻防入门:从攻击者视角构建防御体系

朋友,先问你三个场景,你大概率至少中过其中一个:收到一封“你的账户异常,请点击链接验证”的邮件,顺手就点了;某个平台账号密码被撞库,结果同密码的其他平台也一起失守;一台开了远程…

2026/10/17 7:07:04 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →