dsha-tts-bridge:把 DSH 的语音合成搬到宿主侧

前言

DSH 在安卓上常跑在 proot 容器里:容器内 /dev/snd 不可访问,ffplay、aplay 这类播放命令全部失灵;而部分 ROM 的 Android WebView 又拿不到中文语音包,speechSynthesis.speak() 会直接静默。两条常见的朗读路径都不成立。

唯一稳定可用的方式,是在页面里用 <audio> 播放一个 MP3。下面介绍的这个插件就做了这件事:在 DSH 宿主进程里把文字合成为 MP3,通过 DSH 自带的 webServer 暴露同源路由,页面把 src 指过去即可。它由 flyinghouse-ma 维护。

这是什么

dsha-tts-bridge 是宿主侧的语音合成桥,只做一件事:文字 → MP3,然后由页面负责播放。

它不依赖 API Key、不依赖账号,走的是微软 Edge 朗读的 WebSocket 协议(api.msedgeservices.com,旧版端点是 speech.platform.bing.com),并非官方 Azure TTS SDK。插件自行实现了 WebSocket 客户端(lib/websocket.js),因此零 npm 依赖。

核心功能

  1. 在宿主进程内合成语音,结果以 MP3 返回,页面用 <audio> 就能播。
  2. 通过 DSH 自带的 webServer 暴露同源路由,WebView 内可直接引用,不存在跨域问题。
  3. 内存中对最近 40 条合成结果做 LRU 缓存,同一句话反复播报不会重复请求。
  4. 单次合成 15 秒超时,失败即返回错误,不会卡住页面。
  5. 成功返回 audio/mpeg;失败返回 502 加纯文本原因。

安装与启用

插件通过 DSH 的 profile 机制安装:

dsh plugin --profile web add @flyinghouse-ma/dsha-tts-bridge

装完需要重启 dsh web,因为它是宿主侧模块,热加载不生效。

典型用法

装好之后,任何页面(含 WebView)都可以直接用 <audio> 播报:

<audio src="/dsh-tts/say.mp3?text=需要你确认一下" autoplay></audio>

需要控制音量或复用音频时,可以用 fetch 取回 Blob 再播放:

const r = await fetch('/dsh-tts/say.mp3?text=' + encodeURIComponent('你好'));
const url = URL.createObjectURL(await r.blob());
new Audio(url).play();

服务端参数如下:

参数 说明
text 要念的文字,上限 200 字
voice 可选音色,默认 zh-CN-XiaoxiaoNeural(晓晓,中文女声)

200 字上限是按“提醒播报”场景设定的,目的是防止该路由被当成通用 TTS 代理滥用。

适用场景与注意

适合以下情况:DSH 运行在安卓 proot 容器中、需要短语音播报(审批提醒、状态播报)的开发者;或者 WebView 语音不可用、需要稳定朗读路径的场景。

几点需要注意:

  1. 需要能访问微软的语音服务端点,网络不通时会返回 502,这是已知限制。
  2. 插件只负责合成,播放控制(音量、打断、队列)由页面自行决定。
  3. 插件以当前 dsh 进程的权限运行,安装前应先阅读源码、确认许可证(MIT),再决定是否引入。
  4. 长文朗读不适用于此插件(单条 200 字上限)。

结尾

dsha-tts-bridge 解决的是一个很窄但确实存在的问题:容器里没有音频设备、WebView 又念不出中文时,用一个同源 MP3 路由把播报恢复可用。它不追求通用 TTS 能力,接口和限制都写得很明确。

目录页:https://www.skillhub.cn/plugins/flyinghouse-ma/dsha-tts-bridge

源代码:https://github.com/flyinghouse-ma/dsha-tts-bridge

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜