WEB 六月 23, 2024

Web Speech API 之 Speech Synthesis

文章字数 11k 阅读约需 10 mins. 阅读次数

Speech synthesis

Speech synthesis(语音合成,也被称作是文本转为语音,英语简写是 TTS)包括接收 app 中需要语音合成的文本,再在设备扬声器或音频输出连接中播放出来这两个过程。

Web Speech API 对此有一个主要控制接口 —— SpeechSynthesis ,外加一些处理如何表示要被合成的文本 (也被称为 utterances),用什么声音来播出 utterances 等工作的相关接口。同样的,许多操作系统都有自己的某种语音合成系统,在这个任务中我们调用可用的 API 来使用语音合成系统。

Demo

为了展示 Web 语音合成的简单使用,我们提供了一个例子 —— Speak easy synthesis 。例子是一套表单控件,包括输入需要被合成的文本,设置音调、语速和说出文本时需要的语音。在输入文本之后,按下 Enter/Return 键使它播放。

UI

想跑这个例子,你可以 git clone Github 仓库中的部分 (或者直接下载),在桌面版支持的浏览器打开 index.html 文件,或者在移动端浏览器直接导向 live demo URL ,像 Chrome 和 Firefox OS。

浏览器兼容性

api.SpeechSynthesis

HTML 和 CSS

HTML 和 CSS 还是无足轻重,只是简单包含一个标题,一段介绍文字,以及一个表格带有一些简单控制功能。select 元素初始是空的,之后会通过 JavaScript 使用 option 填充。

<h1>Speech synthesiser</h1>

<p>
  Enter some text in the input below and press return to hear it. change voices
  using the dropdown menu.
</p>

<form>
  <input type="text" class="txt" />
  <div>
    <label for="rate">Rate</label
    ><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate" />
    <div class="rate-value">1</div>
    <div class="clearfix"></div>
  </div>
  <div>
    <label for="pitch">Pitch</label
    ><input type="range" min="0" max="2" value="1" step="0.1" id="pitch" />
    <div class="pitch-value">1</div>
    <div class="clearfix"></div>
  </div>
  <select></select>
</form>

JavaScript

让我们看看 JavaScript 在这个 app 中的强大表现。

设置变量

首先我们获得 UI 中涉及的 DOM 元素的引用,但更有趣的是,我们获得了Window.speechSynthesis 的引用。这是 API 的入口点 —— 它返回了SpeechSynthesis 的一个实例,对于 web 语音合成的控制接口。

var synth = window.speechSynthesis;

var inputForm = document.querySelector("form");
var inputTxt = document.querySelector(".txt");
var voiceSelect = document.querySelector("select");

var pitch = document.querySelector("#pitch");
var pitchValue = document.querySelector(".pitch-value");
var rate = document.querySelector("#rate");
var rateValue = document.querySelector(".rate-value");

var voices = [];

填充 select 元素

为使用设备上可用的不同的语音选项填充 select 元素,我们写了一个 populateVoiceList() 方法。首先调用 SpeechSynthesis.getVoices() ,这个函数返回包含所有可用语音 (SpeechSynthesisVoice对象) 的列表。接下来循环这个列表,每次创建一个 option 元素,设置它的文本内容以显示声音的名称(从 SpeechSynthesisVoice.name 获取),语音的语言(从 SpeechSynthesisVoice.lang 获取),如果某个语音是合成引擎默认的 (检查 SpeechSynthesisVoice.defaulttrue 的属性) 在文本内容后面添加 -- DEFAULT

对于每个 option 元素,我们也创建了 data- 属性,属性值是语音的名字和语言,这样在之后我们可以轻松获取这个信息。之后把所有的 option 元素作为孩子添加到 select 元素内。

function populateVoiceList() {
  voices = synth.getVoices();

  for (const voice of voices) {
    const option = document.createElement("option");
    option.textContent = `${voice.name} (${voice.lang})`;

    if (voice.default) {
      option.textContent += " — DEFAULT";
    }

    option.setAttribute("data-lang", voice.lang);
    option.setAttribute("data-name", voice.name);
    voiceSelect.appendChild(option);
  }
}

早期版本的浏览器不支持 voiceschanged 事件,只有当 SpeechSynthesis.getVoices() 被触发时才返回语音列表。
而其他浏览器,比如 Chrome 中,你必须等待 voiceschanged 事件触发后才能获得可用语音列表。
为了兼容这两种情况,我们运行如下代码:

populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
  speechSynthesis.onvoiceschanged = populateVoiceList;
}

说出输入的文本

接下来我们创建一个事件处理器(event handler),开始说出在文本框中输入的文本。我们把 onsubmit 处理器挂在表单上,当 Enter/Return 被按下,对应行为就会发生。我们首先通过构造函数创建一个新的 SpeechSynthesisUtterance() 实例 —— 把文本输入框中的值作为参数传递。

接下来,我们需要弄清楚使用哪种语音。使用 HTMLSelectElement selectedOptions 属性返回当前选中的 <option> 元素。然后使用元素的data-name属性,找到 SpeechSynthesisVoice 对象的name匹配data-name 的值。把匹配的语音对象设置为SpeechSynthesisUtterance.voice 的属性值。

最后,我们设置 SpeechSynthesisUtterance.pitchSpeechSynthesisUtterance.rate 属性值为对应范围表单元素中的值。哈哈所有准备工作就绪,调用 SpeechSynthesis.speak() 开始说话。把 SpeechSynthesisUtterance 实例作为参数传递。

inputForm.onsubmit = function(event) {
  event.preventDefault();

  var utterThis = new SpeechSynthesisUtterance(inputTxt.value);
  var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
  for (const voice of voices) {
    if (voice.name === selectedOption) {
      utterThis.voice = voice;
    }
  }
  utterThis.pitch = pitch.value;
  utterThis.rate = rate.value;
  synth.speak(utterThis);

在事件处理器的最后部分,我们加入了一个 SpeechSynthesisUtterance.onpause 处理器,来展示SpeechSynthesisEvent 如何可以很好地使用。当 SpeechSynthesis.pause() 被调用,这将返回一条消息,报告该语音暂停时的字符编号和名称。

utterThis.onpause = (event) => {
  const char = event.utterance.text.charAt(event.charIndex);
  console.log(
    `Speech paused at character ${event.charIndex} of "${event.utterance.text}", which is "${char}".`,
  );
};

最后,我们在文本输入框添加了 blur() 方法。这主要是在 Firefox 操作系统上隐藏键盘

  inputTxt.blur();
}

更新 pitch 和 rate 的显示数值

代码的最后部分,在每次滑动条移动时,更新 pitch/rate 在 UI 中展示的值。

pitch.onchange = () => {
  pitchValue.textContent = pitch.value;
};

rate.onchange = () => {
  rateValue.textContent = rate.value;
};

参考资料

0%