# [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)