Firefox 和 Web 语音 API

语音合成和识别是电脑上强大的工具,在现代社会中已非常普遍 - 例如,在流行的现代操作系统上查看 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 的识别和合成部分位于同一规范中,但彼此独立运行。没有什么能阻止您实现一个应用程序,该应用程序识别输入的语音命令,然后将其说回给用户,但除此之外,它们的功能是独立的。

每个部分都有一系列接口来定义其功能,其中核心是一个控制器接口 - 被称为(可以预见地) SpeechRecognitionSpeechSynthesis。在接下来的部分中,我们将探讨如何使用这些接口来构建支持语音的应用程序。

浏览器支持的更多细节

如上所述,到目前为止,已经实现 Web 语音的两个浏览器是 Firefox 和 Chrome。Chrome/Chrome 移动版从版本 33 开始支持合成和识别,后者带有关联 webkit 前缀。

另一方面,Firefox 支持 API 的两个部分,且不带前缀,但需要牢记一些事项

  • 即使识别已在 Gecko 中实现,但目前在桌面/Android 中不可用,因为允许用户授予应用程序使用它的权限的 UX/UI 尚未实现。
  • 语音合成在 Android 上尚不可用。
  • 要在 Firefox (桌面/Android) 中使用规范的识别和合成部分,您需要在 about:config 中启用 media.webspeech.recognition.enablemedia.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 工作,并且喜欢演奏重金属鼓和喝好啤酒。他住在英国曼彻斯特附近,和他的爱人和三个美丽的孩子住在一起。

Chris Mills 的更多文章...


26 条评论

  1. Brett Zamir

    很高兴看到这方面的势头正在增长。桌面支持的大致 ETA 是什么?

    2016 年 1 月 21 日 上午 10:09

    1. Chris Mills

      合成在桌面端有效,尽管需要隐藏一个标记(我已经添加了有关此标记的详细信息!)。

      识别需要一些工程工作……我将请工程师对此事进行权衡。

      2016 年 1 月 22 日 上午 12:28

  2. vince

    演示在 Firefox Android 上不起作用。

    2016 年 1 月 21 日 上午 10:47

    1. Chris Mills

      抱歉 - 我应该在偏好设置方面更清晰。我在顶部附近的注释中添加了一行,说明需要启用的偏好设置。目前,桌面或 Android 上的识别功能不起作用。

      2016 年 1 月 21 日 下午 12:40

  3. Aurelio De Rosa

    你好,Chris。

    我一直关注着这个惊人的 API,从一开始就一直在使用它,我也用它制作了一些演示。在阅读您的文章时,我发现您提到了 Firefox 对语法的支持,感到非常惊讶。规范很久没有更新了,关于语法方面的信息是

    “编辑备注:该小组目前正在讨论应该支持哪些语法格式,如何指定内置语法类型以及未指定时的默认语法。”
    来源:https://dvcs.w3.org/hg/speech-api/raw-file/tip/webspeechapi.html#speechreco-speechgrammar

    我的问题是:JSpeech 语法格式是所有浏览器都采用的格式(因此,规范需要更新)还是仅仅是 Firefox 采用?如果这是大家同意的格式,请问何时达成一致(即日期)以及您是否知道可以查看的任何链接/文章/资源?

    谢谢

    2016 年 1 月 21 日 下午 2:35

  4. Michael Müller

    我有两个问题,我想自己测试一段时间,但可能直接问会更快

    1. SpeechSynthesis.speak() 从何时起在 Firefox OS 上得到支持?https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesis/speak 以及其他几个页面都说 2.5,但我可以在我的 2.0 版本设备上确认它有效。
    2. 是否支持 SSML,支持程度如何?https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisUtterance/text 中说:“文本可以以纯文本或格式良好的 SSML 文档形式提供。不支持 SSML 的设备将剥离 SSML 标记。”

    2016 年 1 月 22 日 上午 12:09

    1. Chris Mills

      我只在 2.5 上测试过;我被告知这是支持开始的时间。如果它在 2.0 上有效,那么我需要更新支持表格;-)

      我不知道我们对 SSML 的支持程度。我会尝试让开发者回答这个问题。

      2016 年 1 月 26 日 上午 2:13

  5. Kelly Davis

    Brett Zamir,我们还没有桌面端的 ETA,抱歉。

    2016 年 1 月 22 日 上午 2:51

  6. Kelly Davis

    Aurelio De Rosa

    据我所知,JSpeech 语法格式只被 Firefox 采用。

    通常,语法用于将可能的语句范围限制在一个可管理的有限集合中,从而简化了识别的计算。

    其他浏览器(例如 Chrome)根本不使用语法。它们使用基于服务器的识别,该识别能够利用大量的 CPU 周期资源,从而无需语法要求。

    我们在设备上进行识别。因此,我们需要语法。我们的目标设备(无论是 Flame 还是资源更受限的设备)都不能保证具有 Google 云中提供的 CPU 资源。

    2016 年 1 月 22 日 上午 3:08

  7. Nick Tulett

    如何测试语音输出是否正确?

    2016 年 1 月 25 日 上午 9:33

    1. Chris Mills

      您的意思是“测试合成器发出的输出,以确保在播放给用户之前它是否正确”?我不确定这是否真的可行 - 合成使用平台的默认合成工具,因此我认为它应该可以正常工作。从查看规范来看,我认为我们最接近的是 https://mdn.org.cn/en-US/docs/Web/API/SpeechSynthesisUtterance/onstart(您可以在开始时暂停语音,检查语句是否包含要朗读的正确文本,如果一切正常,则继续。但我不确定这是否就是您的意思。)

      2016 年 1 月 26 日 上午 2:11

  8. Stebs

    在 Linux 上使用桌面 Nightly,演示不起作用(已在 about:config 中启用两个设置)。
    好的,语音识别在桌面端不起作用,但合成呢,它是否需要 espeak 或其他东西?

    2016 年 1 月 25 日 上午 9:58

    1. Chris Mills

      它将需要某种语音合成引擎才能存在于平台上。Linux(或某些版本)默认情况下没有吗?

      2016 年 1 月 26 日 上午 2:12

  9. Nick Tulett

    我从系统/回归测试的角度考虑问题。

    我可以轻松检查文本输出,但图形输出不太容易,但也能做到。

    您从哪里开始处理音频?您只是手动确认结果,然后将编码后的语音引擎请求用作您的回归测试数据吗?

    2016 年 1 月 26 日 下午 4:57

  10. Matěj Cepl

    关于 Linux……Orca 正在运行,屏幕阅读功能正在运行,但当我打开
    http://mdn.github.io/web-speech-api/speech-color-changer/ 页面时,出现错误

    ReferenceError: webkitSpeechRecognition 未定义

    这是使用 Firefox 45(Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0)

    2016 年 1 月 26 日 上午 6:36

    1. Chris Mills

      是的,我担心规范的识别部分在 Firefox 桌面端还不支持;目前只有 Firefox OS 支持。

      2016 年 1 月 27 日 上午 1:51

  11. Eitan

    嗨,Nick,

    这是一个好问题!在 Firefox 中,我们使用一个虚拟语音服务进行测试。您可以查看我们的测试以获取一些示例:https://dxr.mozilla.org/mozilla-central/source/dom/media/webspeech/synth/test

    网页开发者如何进行自动化测试?语音合成后端是可扩展的。开发者可以创建语音服务扩展,并访问另一端的实际输出。有关语音服务扩展的示例,请参见:http://blog.monotonous.org/2015/04/29/espeak-web-speech-api-addon/

    2016年1月26日上午9:39

  12. Stebs

    好的,我成功在linux上配置了语音合成,我安装了espeak(通常默认情况下不会安装,但存在于大多数发行版的软件包管理器中)。
    不过需要重启。

    2016年1月28日下午12:22

    1. Chris Mills

      啊,这是个好消息 - 感谢您的积极报告!

      2016年1月29日凌晨3:30

  13. Jefbinomed

    我尝试在安卓上运行它,但它仍然无法工作……我不明白,我在beta/nightly上激活了标志。看看我在远程调试中得到的结果,对于beta,SpeechRecognition对象根本不存在。对于nightly,当我尝试使用SpeechRecognition对象时,即使它看起来存在,它也会显示“SpeechRecognition未定义”……。

    我重新启动了应用程序,但没有任何改变……?我错过了什么?

    我使用的是lolipop上的nexus 4。

    此致

    2016年1月29日凌晨1:41

    1. Chris Mills

      API的识别部分目前无法在Android上使用(如上面的一行所述——“即使识别在Gecko中实现,但目前在桌面/Android上不可用,因为允许用户授予应用程序使用它的权限的UX/UI尚未实现。”)。

      然而,合成部分应该可以在Firefox Android上运行,但演示目前似乎失败了。我正在调查此事,并会尽快回复您。

      2016年1月29日凌晨3:51

    2. Chris Mills

      好的,事实证明语音合成部分目前在Android上无法使用。对此表示歉意。我会适当地更新文章。

      2016年2月1日凌晨1:55

  14. Dmitri Levitin

    在Firefox桌面版上,如果设置about:config标志,我可以在Firefox重启后让合成工作,但识别不行。找不到webkitSpeechRecognition或SpeechRecognition。您知道什么时候能修复这个问题吗?

    2016年1月31日下午6:55

    1. Chris Mills

      如文章中所述,识别目前无法在桌面版上运行,因为部分UX/UI尚未解决。目前还没有修复此问题的ETA。

      2016年2月1日凌晨1:58

  15. Michael Gorham

    +1,让SpeechRecognition进入Firefox桌面版。我们能追踪这个功能请求的Bugzilla报告吗?

    (如果将SpeechRecognition置于标志后面,为什么需要解决权限UI?)

    2016年2月16日上午10:06

  16. Mido Basim

    感谢Chris的帖子。我期待着看到这方面的进展。
    +1,同意Michael Gorham所说,但我认为Chris的意思是启用麦克风的UI尚未完成(即浏览器向用户请求使用麦克风权限的部分)。
    我跟踪了这两个票证:https://bugzilla.mozilla.org/show_bug.cgi?id=1244237https://bugzilla.mozilla.org/show_bug.cgi?id=1248897
    截至撰写本文时,两者都尚未进行。我希望Firefox团队尽快解决这个问题:D。
    干杯。

    2016年2月18日凌晨2:24

本文的评论已关闭。