Day 23 - Speech Synthesis

前言

JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教学影片里,建立30个JavaScript的有趣小东西。

另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。


本日目标

还记得在 Day 20 - Native Speech Recognition,我们使用了 Web Speech API 中语音识别(Speech recognition)的部分,而这次我们要使用的是语音合成(Speech synthesis)的部分。

这次的主角是Web Speech APISpeechSynthesisSpeechSynthesisUtterance

在下面的范例,我们会利用 API 语音合成功能,让网页用不同的声音读出textarea里面的文字。


解析程序码

HTML 部分

select(#voices)是一个用来选择要用什麽声音读出文字的选单。

input([name="rate"]) 是用来调整读字的速度。

input([name="rate"]) 是用来调整读字音高。

textarea([name="text"]) 是要读的文字。

button(#stop) 是停止读的按钮。

button(#speak) 是开始读的按钮。

<div class="voiceinator">

    <h1>The Voiceinator 5000</h1>

    <select name="voice" id="voices">
        <option value="">Select A Voice</option>
    </select>

    <label for="rate">Rate:</label>
    <input name="rate" type="range" min="0" max="3" value="1" step="0.1">

    <label for="pitch">Pitch:</label>

    <input name="pitch" type="range" min="0" max="2" step="0.1">
    <textarea name="text">Hello! I love JavaScript ?</textarea>
    <button id="stop">Stop!</button>
    <button id="speak">Speak</button>

</div>

JS 部分

宣告常数 msg 并建立一个SpeechSynthesisUtterance物件,这个物件主要包含两种资讯,一个是要读出的内容(what to read),另一个是要如何读它(how to read)。

宣告空阵列 voices,之後我们会放入由SpeechSynthesis.getVoices()取得的声音种类阵列。

後面的四个常数分别取得声音种类选单、调整速度/音高的滑动杆、开始读出文字按钮、停止读出文字按钮。

const msg = new SpeechSynthesisUtterance(); //come from WEB Speech API
let voices = [];
const voicesDropdown = document.querySelector('[name="voice"]');
const options = document.querySelectorAll('[type="range"], [name="text"]');
const speakButton = document.querySelector('#speak');
const stopButton = document.querySelector('#stop');

textSpeechSynthesisUtterance的一个属性,用来指定要读出的内容,下面我们指定要读出的内容是textarea的文字。

msg.text = document.querySelector('[name="text"]').value;

接下来我们需要取得网页支援的声音种类并把它们做成选项放到选单里面。

在做选单前,我们还要确保真的有拿到声音种类的阵列,所以在speechSynthesis注册voiceschanged event listenerpopulateVoices()作为事件处理函式。

populateVoices()里,我们首先将取得的声音阵列放到voices里面,在 console 印出阵列内容的话,可以发现阵列中的元素都是SpeechSynthesisVoice物件,这个物件拥有该种声音的名称、语言等等的属性。

然後我们可以把 voices 阵列进行 map,把每种声音都做成选项,因为 map 结束是一个阵列,还要加上join()让它变成 HTML 格式的大字串,之後才能放入voicesDropdown当作选项。

function populateVoices(){
    voices = this.getVoices();
    console.log(voices);
    voicesDropdown.innerHTML = voices
    .map(voice => `<option value="${voice.name}">${voice.name} (${voice.lang})</option>`)
    .join('');
}
 
speechSynthesis.addEventListener('voiceschanged',populateVoices);

voices阵列的内容 :

toggle()用来开始读出文字或停止读出文字,我们预设参数startOver = true表示在正常情况下都要开始读文字。方法最上面写speechSynthesis.cancel();的功能是停止读文字,因为希望每一次读都是重新开始读。

function toggle(startOver = true){
    speechSynthesis.cancel(); // stop speaking
    if(startOver){
      speechSynthesis.speak(msg); // restart speaking
    }
}

在选单(voiceDropdown)注册change event listener,用来把读文字的声音种类,改成我们选择的那种。

事件处理函式setVoice(),用来调整读文字时的声音种类。前面有说过SpeechSynthesisUtterance可以决定 how to read,所以它有一个voice属性可以指定声音种类。

要注意的是我们指定的声音种类要是一个SpeechSynthesisVoice物件而不是给它声音的名称就好,所以还要在voices阵列寻找拥有和选项的value一样声音名称(name)的SpeechSynthesisVoice物件。

在方法最後呼叫toggle(),就可以发现随着每一次选的选项不同,它会重新读一次文字。

function setVoice(){
    console.log('changing voice');
    msg.voice = voices.find(voice => voice.name === this.value);
    toggle();
}

voicesDropdown.addEventListener('change', setVoice);

倒数第二个要处理的是读文字时的速度和音高调整,因为SpeechSynthesisUtterance本身有pitchrate属性可供修改,所以我们只要拿设定好的<input>的 name 和 value 来修改SpeechSynthesisUtterance对应的属性值就好。

在两个<input>上都注册change event listener,可以让每一次滑杆值有改变时,就重新指定声音属性并重新开始读文字(别忘记在setOption()的最後放入toggle())。

function setOption(){
    console.log(this.name,this.value);
    msg[this.name] = this.value;
    toggle();
}

options.forEach(option => option.addEventListener('change',setOption))

最後就是关於开始和停止读文字钮的部分啦~

开始读文字的按钮比较简单,只要注册click event listener然用toggle()处理就好。

认真要说的话,停止读文字的按钮也没有难到哪里,只是这边我们不能直接用... , toggle(false),而是要写... , ()=>toggle(false)才会有停止读文字的效果。

speakButton.addEventListener('click',toggle);
stopButton.addEventListener('click',() => toggle(false));
补充资料:

Web Speech API
SpeechSynthesis
SpeechSynthesisUtterance
SpeechSynthesisVoice

范例网页请按此


<<:  [DAY 10] _软件实现I2C协议

>>:  Day8-169. Majority Element

[30天 Vue学好学满 DAY11] v-on

v-on 监听DOM并进行触发 v-on: -> 简写 @ v-on:click="...

DAY17 - 第五个小范例 : 图片加浮水印

前言 今天是铁人赛的第十七天,要来做一个临时的side project : 批次在图档上加浮水印 动...

Backtrader - 绘图

以下内容皆参考 Backtrader 官网 有了策略可以让我们进行评估,有了历史资料可以进行回测,加...

[Lesson15] RxJava

在 gradle (Module) 层级的 dependencies 中内加入: implement...

Day 11 -资料查询语言 WHERE !

我们前几篇介绍了资料操纵 DML 的语法,之後几篇呢我会精选几个比较常用的资料查询 DQL 语法来跟...