iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
0
Modern Web

寫JS30天系列 第 24

JS30 - 24 - Speech Synthesis

還記得幾天前我們有將語音專程文字
今天我們要將文字轉成語音
使用的是Web Speech APISpeechSynthesisUtterancespeechSynthesis

SpeechSynthesisUtterance

SpeechSynthesisUtterance代表一個發音的需求,包含發音的內容和如何辨識的方法,例如textratepitchlang

  1. .lang:設定或取得發音的語言。
  2. .pitch:設定或取得發音的音調。
  3. .rate:設定或取得發音的速度。
  4. .text:設定或取得發音的文字內容。
  5. .voice:設定或取得發音的聲音。
  6. .volume:設定或取得發音的音量。

SpeechSynthesis

SpeechSynthesis是用來控制發音服務,可以取得合成聲音的資訊,並可以控制其發音的狀態
可以想像SpeechSynthesis有一個utterance queue來放置要被讀的資料

先了解他的屬性

  1. SpeechSynthesis.paused:如果回傳true,表示現在是暫停狀態
  2. SpeechSynthesis.pending:如果回傳true,表示還有未讀完的語句在等著被讀
  3. SpeechSynthesis.speaking:如果回傳true,表示有發音正在被讀,即使是在暫停的狀態下
    再來是他的方法
  4. .speak:加一個發音到utterance queue,他將在上一個發音讀完時接著讀
  5. .cancal:清空utterance queue
  6. .pasue:進入暫停狀態(暫停發音)
  7. .resume:進入非暫停狀態(接著發音)

首先我們要建構一個SpeechSynthesisUtterance物件
並且需要一個陣列來存放我們的資訊

const msg = new SpeechSynthesisUtterance();
//建構一個SpeechSynthesisUtterance物件
let voices = [];
//存放發音的相關資訊
msg.text = document.querySelector('[name="text"]').value;
//將使用者輸入的內容存到msg內
function populateVoice() {
    console.log("Hey!")
}

speechSynthesis.addEventListener('voiceschanged', populateVoice);

speechSynthesis.onviceschanged事件
要特別注意的是voiceschanged在一開始跟伺服器端被選擇(例如google提供的)或者在客戶端被安裝/解除安裝時,會執行一次
之後,這個事件會因為.getVoices()而觸發
當觸發的時候我們就要更新可以選擇的voice list
並innerHTML

function populateVoice() {
    voices = this.getVoices();
    voicesDropdown.innerHTML = voices
        .map(voice => `<option value="${voice.name}">${voice.name} (${voice.lang})</option>`)
        .join()
}

接著我們要將選定的voice放到msg內
.find()去找到對應的voice
這裡有一個有趣的地方是this.value
this指向的是<select>
而他的value是存在子層的<option>
這個跟和是一組的有關

接著做點擊不同按鈕時讓他發聲
首先我們先使用.speak()讓我們在點擊speak button時可以發聲
而參數則是我們設定已久的msg

這時妳會發現連點的時候並不會直接發聲
而是等上一句念完
才會處理第二次點擊的聲音
(本身的特性)
這時候我們只要在.speak()之前加上.cancel()
把之前的話都清空即可每次連點都直接發聲
最後再加上一點微調
這個toggle()就可以廣泛被利用
用參數startOver來存布林值
true(預設)會播放聲音
這樣的話
就可以在「需要重新播放」時,使用toggle()(上方切換voice也可以使用)
而「需要中止播放」,只要將startOver設定為false即可只中止不播放

const speakButton = document.querySelector('#speak');
const stopButton = document.querySelector('#stop');
function toggle(startOver = true) {
    speechSynthesis.cancel();
    if (startOver) {
        speechSynthesis.speak(msg);
    }
}

speakButton.addEventListener('click', toggle);
stopButton.addEventListener('click', toggle.bind(null, false));

由於在addEventListener內不能加參數
所以我們使用.bind()method
.bind()的第一個參數是指定target function的this,也就是.toggle()的作用對象
第二個參數開始則是我們target function的參數
因為.toggle()只有一個參數
所以.bind()第一個參數傳null,第二個參數傳false

有了上面的基礎
使用.pause()來暫停和.resume()來恢復播放也是輕輕鬆鬆

const pauseButton = document.querySelector('#pause');
const resumeButton = document.querySelector('#resume');
function pauseMessage() {
  speechSynthesis.pause();
}
function resumeMessage() {
  speechSynthesis.resume();
}
pauseButton.addEventListener('click', pauseMessage);
resumeButton.addEventListener('click', resumeMessage);

最後我們可以來調整說話的速度、音調及內容
只要有調整到以上三點的
都會調整他們存儲在msg內的值
並馬上調用.toggle()

const options = document.querySelectorAll('[type="range"], [name="text"]');
function setOption() {
  msg[this.name] = this.value;
  toggle();
}
options.forEach(option => option.addEventListener('change', setOption));

這樣就大功告成了!

[Demo](https://jasonyangbanana.github.io/JS30/23 - Speech Synthesis/index-START.html)
完整程式碼


上一篇
JS 30 - 23 - Follow Along Link Highlighter
下一篇
JS30 - 25 - Sticky Nav
系列文
寫JS30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言