本周开发了一个有趣的新功能——语音合成。本博客已经抢先使用啦!归功于百度AI提供的接口,现在本博客也可以开口说话了。点击文章的“朗读”功能即可为您朗读本文。
其实在网页上要实现语音合成有至少两种思路:一是使用 HTML5 的接口,另一个则是使用专业合成接口(例如我就使用了百度的接口)。下面单独介绍下。
浏览器语音合成
早在HTML5中,就已经提供了语音合成的API。听起来非常的高大上,其实也就是很多年前就已经有的TTS(Text To Speech )引擎。用起来是非常的方便,请看下面一段代码:
var tts = new SpeechSynthesisUtterance();
tts.text = '您好,欢迎光临我的博客!';
speechSynthesis.speak(tts);
可以看到,使用HTML5的接口,只需要非常简短的代码,就可以让浏览器读出文本内容。不过最主要的感受是:语音实在是太机械了。其实这还不是最大的问题,浏览器能不能准确的朗读,完全取决于您的操作系统安装了哪些TTS语音包。例如某些精简版的Windows就去除了这个功能,也就无法直接使用了。因此这种方式有局限性。(HTML5的语音合成API详细资料详见本文最后的“参考文档”部分)
百度语音合成
除了浏览器的合成方式以外,现在也有非常多的公司提供了免费的语音合成接口,例如:百度、阿里、讯飞等等。它们合成的声音相对上面介绍的方式就好听多了,例如百度语音合成就可以选择男女生音色、是否带有情感等等。不过用起来也复杂多了,不可能像上面一样几行代码就搞定。(百度语音合成接口详细资料详见本文最后的“参考文档”部分)
由于百度已经提供了相关的开发文档,因此写本文的目的并不是教会WordPress开发者怎么使用百度语音合成的接口。而是在开发过程中遇到了两个坑,在此记录一下,避免其他开发者也踩坑。下面两个问题无论是百度的合成接口还是阿里的合成接口应该都存在的:
1、文本内容长度限制
百度的语音合成接口规定文本内容最多不超过512字符,阿里则是300字符。这都低于一篇文章的文本数量。因此一篇文章被截断为两部分甚至多个部分被合成是肯定存在的。因此生成了多个音频文件,要如何让用户播放,这就是问题了。我在此提供两种解决办法:
- 方案一:后端将文章多个部分的语音文件从百度的服务器上请求下来,将这些文件按顺序拼接成一整个文件,然后再提供给浏览器前端播放。
- 方案二:浏览器将多个语音文件当成播放列表,依次播放。
显然,第一种方案的用户体验更好!无论你的文章被截断成了几个部分,浏览器播放的时候都只得到一个文件。不会在播放完上一条语音后,加载下一条语音前出现等待的加载空闲时间。
然而我在开发的时候选择了第二种方式,原因也非常简单:我的主机每月的流量是固定的,如果使用方案一让后端将语音连接后再输出,势必会让我的服务器承担更多的流量消耗。而如果采用第二种方式,浏览器直接从百度获取文件,我就不用担心流量的问题了。和钱比起来,降低一点用户体验好像也没什么关系。毕竟我的博客从来就是不盈利的。
注:使用方案二的时候,需要考虑如何播放一个播放列表的问题。我的解决方案是监听播放完成时的事件,完成后加载下一条音频。参考下面的部分代码:
{
audio: new Audio(),
audioArray:['http://xxx','http://xxx'],
speak(textArray) {
//...
this.audio.addEventListener('ended', this.playEndedHandler, false);
//...
},
playEndedHandler() {
if (this.audioArray.length > 0) {
this.audio.src = this.audioArray.pop();
this.audio.play();
} else {
this.stop();
}
},
play() {
//...
this.audio.play();
//...
},
pause() {
//...
this.audio.pause();
//...
},
stop() {
//...
this.audio.removeEventListener('ended',this.playEndedHandler,false);
//...
}
}
2、Safari不能自动播放
苹果系统的 Safari 浏览器为了防止网页上的视频音频通过脚本自动播放影响用户体验,做了非常多的优化。因此必须要用户手动去触发播放按钮,否则浏览器会阻止脚本的播放功能。即使这样,仍然有一种情况也务必需要加入考虑范围:远程请求播放地址然后播放的情况。
这种情况可以理解为:“用户点击页面上的播放按钮→用 jQuery 的 Ajax 从后端请求音频地址→播放这个音频”,逻辑看起来好像也是用户主动触发的。然而在 Safari上这种形式也不行,因为 jQuery 的请求是需要花一定时间的。等请求完成之后使用播放脚本时,Safari 就会认为是脚本自动播放而不是用户手动播放了。
我也只是试了jQuery的Ajax完成时回调,并没有尝试 Promise ,感兴趣的同学可以试一下 Promise 会不会也这样
对于这种情况的解决办法是: 预先将地址请求后存储起来。等用户播放时,直接播放音频地址,这种方法就可以避免这个尴尬了。
好了,以上就是开发语音合成中的一些心得。希望其他开发者有前车之鉴,不要踩和我一样的坑。
可以换个更好听的语音吗?