最近有个项目蛮折磨人的,里面有个需求是将系统发出的消息转成语音信息,一开始简单查了一下,发现其实主流浏览器都有现成的语音合成 api
顺手封装成了一个工具函数,在后续项目中持续使用:
export function speechSpeak(
// 播报消息
msg: string,
// 播报次数
count = 1,
// 播报异常回调
errorCallback?: (error: string) => void
) {
const utterance = new SpeechSynthesisUtterance();
utterance.lang = "zh-CN";
utterance.text = msg;
for (let i = 0; i < count; i++) {
speechSynthesis.speak(utterance);
}
utterance.onerror = (event) => {
console.log("===语音播报异常", event);
// 异常状态码参考 https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisErrorEvent/error
errorCallback && errorCallback(event.error);
};
window.onbeforeunload = function () {
// Chromium won't stop speaking after closing tab.
// So shut up, pls.
speechSynthesis.cancel();
};
}
当然你也可以封装成一个 promise 函数 ~
最好选择离线可用的语音包,否则会受网络影响; 通过
speechSynthesis.getVoices()
检查设备可用的语音包,检查对应的localService
属性.
当时看了下 caniuse
,发现这个语音合成 api 很多浏览器都支持了,而且支持的版本也蛮多,比如 chrome >= 33
,就觉得这个 api 是靠谱的,也就没去想什么了。于是就有了以下踩坑日记..
第一次被恶心到的是浏览器的自动播放声音策略。在最新版移动端 chrome 和 ios 的浏览器上基本无解(有解决办法的欢迎 comment,反正我是查了很多资料都没找着方法)。最终找到的靠谱方法有以下几个,都是通过一些方法关闭用户交互限制:
- pc 端,可以通过设置声音的白名单实现,chrome 和 safari 实践可行(chrome 打开 chrome://settings/content/sound,在允许播放声音里添加网站)
- window pc 可以通过命令行启动 chrome(关闭自动播放策略,
chrome.exe --autoplay-policy=no-user-gesture-required
) - 降级到 chrome 74-76 版本,在
chrome://flag
中关闭用户交互限制。在 pc 或安卓都可以自动播放(ios 没试过,找不到那个版本的包…)
其他未经实践的思路有:
- windows 平板,可能和 windows pc 一样可以配置网页白名单允许自动播放?或者通过命令行启动浏览器?
- 做成移动端应用(react-native/flutter/…)
其实不难看出,自动播放策略在各个系统上的不同版本的浏览器的表现不太一样,目前看来所以最稳妥的方式无疑是做成移动端应用..
如果产品要往外推广的话,肯定要把这个限制干掉。
第二个恶心到我的是经常会偶现不能发出语音,即使是在确保用户有初次交互的情况下。当然,绝大部分是 pad 没收到消息,这里麻烦的点在于怎么定位语音问题,比如怎么确定 pad 有没有收到消息、是不是设备或者浏览器版本限制、是不是网络因素?
场景可以简单描述下,是通过 pad 访问车上的 pc,那这其中肯定就需要 pad 有移动网络。当时是在车上的 pc 有发语音,但 pad 没出声音(在用户交互按钮打开的情况下)。这种情况其实有可能是网络因素导致语音不稳定。所以思路有两点:
- 记录 pad 的语音消息日志,输出到 pad 上。更好的方式是搭建异常监控平台,通过埋点的方式监控语音消息的接收情况,以可视化的形式展示出来,就不用额外占用网页空间
- 但其实如果在车上,也可以通过数据线调试。但我们的测试工程师对于 web 网页调试技能比较欠缺,他们更多的关注系统和应用层面的测试。这块也就不太可行 emm
这以上问题,全是开发和测试过程中不断踩出来的。所以对于语音合成的实现,无非就是以下几个策略
- 无自动播放需求,加个按钮确保页面加载的初次交互
- 使用百度语音合成或者其他合成方式,先转成音频文件发给前端,收到后再播放出来
- 如果需要离线,那只能借助下浏览器的语音合成 api 了,而且还要先查一下是否有对应的离线包(在安卓 pad 上基本都找不到离线的语音包..可能是文件大小限制?)
- 或者可以写个 c ++ 服务,转成 wasm 供前端调用,前提是 c++服务能实现语音合成并输出音频的二进制流给到前端
- 有自动播放需求
- pc 设备可控,配置白名单
- 做成 app,百度或者讯飞都有对应的语音合成离线包(安卓/ios)
当然,如果你要在网页实现自动播放,那要么设备可控、要么浏览器版本可控,限制其实蛮多的,说不定还有更多的坑等着你
以上种种会踩坑也是由于自身知识广度不够,加上调研不充分导致。by the way,持续跟踪这个特性吧,希望未来能更加稳定