语音合成和识别是电脑上强大的工具,在现代社会中已非常普遍 - 例如,在流行的现代操作系统上查看 Cortana、Dictation 和 Siri 等工具,以及屏幕阅读器等辅助功能工具。
但是,Web 呢?能够直接向网页发出语音命令,并让浏览器直接读取文本内容将非常有用。
幸运的是,一些聪明的人一直在研究这个问题。 Web 语音 API 已经存在一段时间了,规范大约在 2014 年编写,此后没有重大变化。截至 2015 年底,Firefox (44+ 在一个 pref 后,以及 Firefox OS 2.5+) 已经实现了 Web 语音,Chrome 也提供了支持!
在本文中,我们将探讨此 API 的工作原理,以及您现在可以获得哪些乐趣。
它是如何工作的?
您可能在想,“像语音合成这样的功能实现起来非常复杂。” 嗯,您是对的。浏览器倾向于默认使用操作系统上提供的语音服务,因此例如,当您在 OS X 上访问 Firefox 或 Chrome 的语音合成时,您将使用 Mac 语音服务。
Web 语音 API 的识别和合成部分位于同一规范中,但彼此独立运行。没有什么能阻止您实现一个应用程序,该应用程序识别输入的语音命令,然后将其说回给用户,但除此之外,它们的功能是独立的。
每个部分都有一系列接口来定义其功能,其中核心是一个控制器接口 - 被称为(可以预见地) SpeechRecognition 和 SpeechSynthesis。在接下来的部分中,我们将探讨如何使用这些接口来构建支持语音的应用程序。
浏览器支持的更多细节
如上所述,到目前为止,已经实现 Web 语音的两个浏览器是 Firefox 和 Chrome。Chrome/Chrome 移动版从版本 33 开始支持合成和识别,后者带有关联 webkit 前缀。
另一方面,Firefox 支持 API 的两个部分,且不带前缀,但需要牢记一些事项
- 即使识别已在 Gecko 中实现,但目前在桌面/Android 中不可用,因为允许用户授予应用程序使用它的权限的 UX/UI 尚未实现。
- 语音合成在 Android 上尚不可用。
- 要在 Firefox (桌面/Android) 中使用规范的识别和合成部分,您需要在 about:config 中启用
media.webspeech.recognition.enable
和media.webspeech.synth.enabled
标志。 - 在 Firefox OS 中,对于要使用语音识别的应用程序,它需要具有特权,并且包含音频捕获和语音识别权限(请参阅此处的适合的清单 示例)。
- Firefox 目前不支持 continuous 属性
- onnomatch 事件处理程序 目前使用有限 - 它不会触发,因为 Gecko 集成的语音识别引擎 Pocketsphinx 不支持每个识别的置信度度量。因此,它不会报告“对不起,这不是其中任何一个” - 而是说“在你给我的选项中,这个看起来最好”。
注意:Chrome 似乎不处理特定的语法;而是只返回所有结果,您可以根据需要处理它们。这是因为 Chrome 的服务器端语音识别比 Firefox 使用的客户端解决方案具有更多可用的处理能力。每种方法都有其优点。
演示
我们编写了两个简单的演示,让您尝试语音识别和合成:语音颜色更改器和轻松说合成。您可以在 Github 上找到这两个演示。
要在线运行它们
语音识别
让我们快速看一下为 语音颜色更改器演示 提供动力的 JavaScript。
Chrome 支持
如前所述,Chrome 目前支持带有关联前缀属性的语音识别,因此我们用此代码开始,以确保每个浏览器都接收了正确的对象(nom nom)。
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
var SpeechGrammarList = SpeechGrammarList || webkitSpeechGrammarList
var SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent
语法
下一行定义了我们希望应用程序识别的语法
var grammar = '#JSGF V1.0; grammar colors; public <color> = aqua | azure | beige | bisque | black | [LOTS MORE COLOURS] ;'
使用的语法格式是 JSpeech 语法格式 (JSGF)。
将语法插入我们的语音识别中
接下来要做的是定义一个语音识别实例,以控制应用程序的识别。这可以通过使用 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognition/SpeechRecognition">SpeechRecognition()</a>
构造函数来完成。我们还创建一个新的语音语法列表,以包含我们的语法,使用 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechGrammarList/SpeechGrammarList" target="_blank">SpeechGrammarList()</a>
构造函数。
var recognition = new SpeechRecognition();
var speechRecognitionList = new SpeechGrammarList();
我们使用 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechGrammarList/addFromString" target="_blank">SpeechGrammarList.addFromString()</a>
方法将语法添加到列表中。它的参数是我们想要添加的语法,以及一个可选的权重值,用于指定此语法相对于列表中其他语法的权重(可以在 0 到 1 之间,包含边界)。添加的语法作为 SpeechGrammar
对象实例在列表中可用。
speechRecognitionList.addFromString(grammar, 1);
然后,我们将 SpeechGrammarList 添加到语音识别实例,将其设置为 SpeechRecognition <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognition/grammars" target="_blank">grammars</a>
属性的值。
启动语音识别
现在,我们实现一个 onclick
处理程序,以便当屏幕被点击/点击时,语音识别服务将启动。这是通过调用 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognition/start" target="_blank">SpeechRecognition.start()</a>
来实现的。
var diagnostic = document.querySelector('.output');
var bg = document.querySelector('html');
document.body.onclick = function() {
recognition.start();
console.log('Ready to receive a color command.');
}
接收和处理结果
语音识别启动后,可以使用许多事件处理程序来检索结果和其他相关信息(请参阅 SpeechRecognition 事件处理程序列表)。您可能最常使用的是 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognition/onresult" target="_blank">SpeechRecognition.onresult</a>
,它会在收到成功结果时触发
recognition.onresult = function(event) {
var color = event.results[0][0].transcript;
diagnostic.textContent = 'Result received: ' + color + '.';
bg.style.backgroundColor = color;
console.log('Confidence: ' + event.results[0][0].confidence);
}
这里的第二行看起来有点复杂,所以让我们一步步解释。<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognitionEvent/results" target="_blank">SpeechRecognitionEvent.results</a>
属性返回一个包含一个或多个 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognitionResultList" target="_blank">SpeechRecognitionResultList</a>
对象的 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognitionResult">SpeechRecognitionResult</a>
对象。它有一个 getter,因此它可以像数组一样访问 - 因此第一个 [0] 返回位置 0 处的 SpeechRecognitionResult
。
每个 SpeechRecognitionResult
对象都包含 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognitionAlternative" target="_blank">SpeechRecognitionAlternative</a>
对象,这些对象包含识别出的单个单词。它们也有 getter,因此它们可以像数组一样访问 - 因此第二个 [0] 返回位置 0 处的 SpeechRecognitionAlternative
。然后,我们返回它的 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechRecognitionAlternative/transcript" target="_blank">transcript</a>
属性以获取一个字符串,其中包含单个识别结果的字符串,将背景颜色设置为该颜色,并在 UI 中报告识别的颜色作为诊断消息。
您可以在 MDN 上找到有关此演示的更多详细信息。
语音合成
现在,让我们快速回顾一下轻松说合成 演示 的工作原理
设置变量
首先,我们捕获对 <a href="https://mdn.org.cn/en-US/docs/Web/API/Window/speechSynthesis" target="_blank">Window.speechSynthesis</a>
的引用。这是 API 的入口点 - 它返回 <a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesis" target="_blank">SpeechSynthesis</a>
的实例,这是 Web 语音合成的控制器接口。我们还创建一个空数组,用于存储可用的系统声音(参见下一步)。
var synth = window.speechSynthesis;
...
var voices = [];
填充选择元素
为了用设备可用的不同语音选项填充<select>
元素,我们编写了一个populateVoiceList()
函数。我们首先调用 SpeechSynthesis.getVoices()
,它返回所有可用语音的列表,以<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisVoice" target="_blank">SpeechSynthesisVoice</a>
对象表示。然后,我们循环遍历此列表 - 对于每种语音,我们创建一个<option>
元素,将其文本内容设置为显示语音的名称(从<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisVoice/name" target="_blank">SpeechSynthesisVoice.name</a>
获取),语音的语言(从<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisVoice/lang" target="_blank">SpeechSynthesisVoice.lang</a>
获取),以及 - 如果语音是合成引擎的默认语音,则为 DEFAULT(通过查看<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisVoice/default" target="_blank">SpeechSynthesisVoice</a>
.default 是否返回 true 来检查)。
function populateVoiceList() {
voices = synth.getVoices();
for(i = 0; i < voices.length ; i++) {
var option = document.createElement('option');
option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
if(voices[i].default) {
option.textContent += ' -- DEFAULT';
}
option.setAttribute('data-lang', voices[i].lang);
option.setAttribute('data-name', voices[i].name);
voiceSelect.appendChild(option);
}
}
当我们运行该函数时,我们会执行以下操作。这是因为 Firefox 不支持<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesis/onvoiceschanged" target="_blank">SpeechSynthesis.onvoiceschanged</a>
,并且在触发SpeechSynthesis.getVoices()
时只会返回一个语音列表。然而,在 Chrome 中,您必须等待事件触发才能填充列表,因此会出现下面的 if 语句。
populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
朗读输入的文本
接下来,我们创建一个事件处理程序来开始朗读输入文本字段中的文本。我们正在表单上使用onsubmit
处理程序,以便在按下 Enter/Return 键时执行操作。我们首先使用其 构造函数 创建一个新的SpeechSynthesisUtterance()
实例 - 它以文本输入的值作为参数传递。
接下来,我们需要弄清楚使用哪种语音。我们使用HTMLSelectElement selectedOptions
属性来返回当前选定的<option>
元素。然后,我们使用此元素的data-name
属性,找到其名称与该属性的值匹配的SpeechSynthesisVoice
对象。我们将匹配的语音对象设置为<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisUtterance/voice" target="_blank">SpeechSynthesisUtterance.voice</a>
属性的值。
最后,我们将<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisUtterance/pitch" target="_blank">SpeechSynthesisUtterance.pitch</a>
和<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisUtterance/rate">SpeechSynthesisUtterance.rate</a>
设置为相关范围表单元素的值。然后,在完成所有必要的准备工作后,我们通过调用<a href="https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesis/speak" target="_blank">SpeechSynthesis.speak()</a>
,并将其SpeechSynthesisUtterance
实例作为参数传递,开始朗读语句。
inputForm.onsubmit = function(event) {
event.preventDefault();
var utterThis = new SpeechSynthesisUtterance(inputTxt.value);
var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
for(i = 0; i < voices.length ; i++) {
if(voices[i].name === selectedOption) {
utterThis.voice = voices[i];
}
}
utterThis.pitch = pitch.value;
utterThis.rate = rate.value;
synth.speak(utterThis);
最后,我们在文本输入上调用blur()
。这主要用于隐藏 Firefox OS 上的键盘。
inputTxt.blur();
}
您可以在 MDN 上找到有关此演示的更多详细信息。
关于 Chris Mills
Chris Mills 是 Mozilla 的高级技术作家,他撰写有关开放式网络应用、HTML/CSS/JavaScript、A11y、WebAssembly 等方面的文档和演示。他喜欢使用网络技术,并偶尔在会议和大学发表技术演讲。他曾为 Opera 和 W3C 工作,并且喜欢演奏重金属鼓和喝好啤酒。他住在英国曼彻斯特附近,和他的爱人和三个美丽的孩子住在一起。
26 条评论