---
# System prepended metadata

title: '[JS30] Day.23 Speech Synthesis'
tags: [JS30]

---

# [JS30] Day.23 Speech Synthesis 
###### tags: `JS30`
## 任務 Task
抓取 `Web Speech API` 的各種語言資料，建立選單，可選擇讀取文字的語音語言，及更改速度及音調高低。

==完成時間：30min==
## 步驟 Step
1. 建立一個新的 `SpeechSynthesisUttrance()` 物件放入變數 `msg`，可以讀取語音資料包含語言、語調等等。
2. 將`textarea` 的文字放入 `msg.text` 裡。 
3. 當 `speechSynthesisVoice` 清單發生變化時，將所有的 `lang` 放入 `option` 建立選單。
```javascript=
speechSynthesis.addEventListener("voiceschanged", populateVoices);
function populateVoices() {
    voices = this.getVoices();
    voicesDropdown.innerHTML = voices
        .filter((voice) => voice.lang.includes("en"))
        .map((voice) =>`<option value="${voice.name}">${voice.name} (${voice.lang})</option>`)
        .join("");
}
```
3. 當選單選擇改變時，將 `msg` 裡的 `voice` 項目，改成符合選項的 `speechSynthesisVoice`。
```javascript=
voicesDropdown.addEventListener("change", setVoice);
function setVoice() {
    msg.voice = voices.find((voice) => voice.name === this.value);
}
```
4. 建立一個 `toggle function` ，設立 `flag` 為 `true` 時，播放語音。在播放前先將前面的語音取消 `speechSynthesis.cancel()`。
5. 建立一個 `setOption function` ，當控制語速、音調的 `input` 以及 `textarea` 發生改變時，將 `msg` 的對應項目，改為更改後的數值，並播放語音。

## 筆記 Note
### <font color=#337EA9>JS SpeechSynthesisUttrance()</font>
* 是一個 `Web Speech API` 的介面，可以控制輸出語音的文字、語言、聲音大小等等。
* **Properties：**

    * `lang`：抓取及更改語言。
    * `pitch`：抓取及更改語調。
    * `rate`：抓取及更改語速。
    * `text`：抓取及更改語音文字。
    * `voice`：抓取及更改 `SpeechSynthesisVoice` 語音物件。
    * `volumn`：抓取及更改語音音量。
### <font color=#337EA9>JS speechSynthesis</font>
* 是一個 `Web Speech API` 的語音服務介面，可以控制語音輸出及停止等等。
* **Properties：**

    * `paused`：如果停止回傳 `true` （boolean）。
    * `speaking`：如果正在播放回傳 `true` （boolean）。
* **Method：**
    * `cancel()`：移除語音。
    * `getVoices()`：回傳一個清單含各個語音的`SpeechSynthesisVoice` 物件。
    * `pause()`：暫停語音。
    * `speak()`：播放語音。
* **Event：**
    * 只有唯一個監聽事件 `voiceschanged` 。
    * `voicechanged event` 監聽當 `SpeechSynthesisVoice` 的清單有改變時觸發。 
## 遇到問題 Problem
:::danger
⚠️ <font color=black>Problem</font>
:::
 **不太懂監聽事件 `voiceschanged`？**
:::info
:ok_hand: <font color=black>Revise</font>
:::
看一下 MDN 的描述：
 >when the list of SpeechSynthesisVoice objects that would be returned by the SpeechSynthesis.getVoices() method has changed 

>當 `SpeechSynthesisVoice` 清單在 `getVoices()`時發生改變就會觸發。

但是 `SpeechSynthesisVoice` 不是都是固定的嗎？要怎麼改變？

回去重聽了一次課程，他說在 `getVoice()` 時是需要時間的，所以一開始會抓不到 `SpeechSynthesisVoice`，喔～～所以其實過幾秒就可以了嘛。

```javascript=
voices = speechSynthesis.getVoices();
function populateVoices(){}
window.setTimeout(populateVoices, 0);
```
先在外面 `getVoices()` 抓 API，再設定 `setTimeout` 就可以抓到回傳的 `voice`。其實在使用 `API` 時本來就會有時間延遲，所以一開始抓不到也是正常。

之前在抓外部 `API` 時會用 `fetch..then..` 的方式抓完才執行，所以提供了 `Voiceschanged Event` 的方式監聽 `getVoice()` 回傳的 `SpeechSynthesisVoice` 從無到有時觸發。

## 連結
[MDN Voiceschanged Event](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/voiceschanged_event)