瀏覽器本身就可以發音
在寫這篇以前,以為想要讓網頁發出聲音,就要接 Google 的什麼 API 之類的,查了一下後,才知道瀏覽器本身就有這個功能,而且聲音也會不一樣,像 Google有 名的機械女聲就是Chrome上才有。
本篇筆記是紀錄 August 的開發過程,以及中間踩到的幾個坑。
頁面製作
接 SpeechSynthesisUtterance 的功能,很久以前就寫了一個 Demo 了,當時是放在 CodePen 上,那時在找幾個頁面樣版,就用了 Material Kit 這套。
這次把頁面優化,並且加上可以手動調整參數的功能,就一樣是用 Material Kit。
最後完成的頁面在這,可以先玩玩看:
https://letswritetw.github.io/letswrite-speech_synthesis_utterance-api/
頁面預計是做成三張卡片,一張是點了按鈕就會發聲,一張是輸入文字後會發聲,最後一張就是發聲的參數調整。
HTML 的部份,因為 Material Kit 的基底是 Bootstrap,所以都直接用它所有的 Gird、Card、Button、Slider 等,UI framework 的好處就是即便就是複製文件中的範例程式碼,頁面第一眼看上去也會有設計感。
SpeechSynthesisUtterance
SpeechSynthesisUtterance 在 MDN 上的說明,最主要的就是一頁:
https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance
原本以為照著用,然後用 Vue.js 把參數接一接,馬上就可以完成的,但,不設參數是還好,複製貼上 code 一下就可以讓瀏覽器講話了。一接參數就踩坑了,光是調整講話速度的倍速(rate),就研究了 2 個多小時。
詳細的程式碼附在文未的程式碼那段,這邊就來寫遇到的坑。
第一坑 拿瀏覽器支援的所有語音
參考文件上寫的,拿所有瀏覽器的語音方式如下:
var synth = window.speechSynthesis; var voices = synth.getVoices();
voices
就會是一個陣列,裡面就是所有支援的語音列表。
這邊是第一個坑,JavaScript 拿語音要時間。
當寫了 var voices = synth.getVoices();
後,就開始對陣列寫個 forEach
去塞進 option
裡,然後就會看見 option
什麼都沒有,因為 voices
還沒抓到陣列,所以 console.log(voices)
會得到的是空陣列。
原本的解決方法是,寫一個 setTimeout
,在 500 毫秒後才寫 var voices = synth.getVoices();
,但遇到的問題就是每個瀏覽器抓到陣列的時間不一樣,500 毫秒也許 Chrome 讀到了,但 FireFox 卻還沒,後來就放棄這個解法,改用之前學到的 Promise。
Promise 的部份可參考這篇:Promise, Async, Await 基本使用筆記
原本是用 Async 寫:
var voices = await synth.getVoices();
但,出來的結果還是空陣列。
後來不死心 Google 了一下,發現網路上有高手寫了 範例,稍微修改後寫成這樣:
var synth = window.speechSynthesis; function setVoices() { return new Promise((resolve, reject) => { let timer; timer = setInterval(() => { if(synth.getVoices().length !== 0) { resolve(synth.getVoices()); clearInterval(timer); } }, 10); }) } setVoices().then(voices => console.log(voices));
首先把取得語音列表的部份寫成 Promise,在 Promise 裡再用 setInterval
,每 10 毫秒去檢查一次 synth.getVoices()
是不是空值,不是的時候才回傳 synth.getVoices()
出來。
實作後,確認這樣做不會得到空值,語音選單可以用 forEach
塞 input 了。
在此我們感謝高手們孜孜不倦的努力及分享。
第二坑 調整速度倍率
文件中裡面寫調整速度倍率的參數是:SpeechSynthesisUtterance.rate
rate
的值是 0.1 – 10,指的是幾倍,比方如果設 1.5,那講話速度就會是平常速度的 1.5 倍。預設值是 1。
文件裡面有一句話一開始有看到,但沒在意,就踩坑了:
Some speech synthesis engines or voices may constrain the minimum and maximum rates further.
「一些語音合成引擎或語音可能會進一步限制最小和最大速率。」
原本想說限制就限制,沒什麼,結果 2 個小時的青春就死在這。
它沒有寫,當超過限制的倍速時,會給的是預設值 1,而且每種語系的最大值是不一樣的。
然後 August 就很苦腦的找為什麼怎麼調 rate
,講出來的速度都沒變?
在 2 個小時的逝去的青春及實驗下,目前得知,在用 Chrome 的時候,最好的設定是 0.1 – 2,不要超過 2,一超過 2,有些語音就會回到預設值的 1,甚至是整個功能就掛掉了,不論怎麼調都不再發聲,得關掉頁籤後再重新打開才行。
第三坑 Chrome 的 Google 語音
如果是用 Chrome 抓語音列表,會看見陣列多出幾個 Google 才有的語音:
Google 國語 講出來的就是卡X諾X新聞的那種女機械音,這些是 Chrome 才會有,FireFox 跟 Safari 是沒有的。
原本想說 Google 好佛心,可以這樣子給使用者玩,後來 August 在測試時,隨便丟了一段新聞的內文用 Google 國語唸,突然,唸到一半就中斷了,而且會發生在點其它語音也不會有反應的狀況。
實測後,發現唸超過 80 幾個字,Google 語音就會斷,然後讓整個語音的功能掛掉,如果是先唸到 79 個字,再讓它唸第二次 79 個字,加起來超過 80 幾也一樣斷。
發現這個狀況後,目前沒找到解法,即便是看 MDN 給的範例頁面也有一樣情況,最後只能忍痛對陣列用 filter 把 Google 語音的給濾掉。
原始碼
Demo 頁的原始碼放到 GitHub 上了:
https://github.com/letswritetw/letswrite-speech_synthesis_utterance-api
Demo 再放一次,歡迎來玩玩:
https://letswritetw.github.io/letswrite-speech_synthesis_utterance-api/
筆記後心得
目前 SpeechSynthesisUtterance,在IE上完全不支援,很好,不用理會那隻上古神獸。
但在看 MDN 的說明文件,有看見後面附了一個 Web Speech API,沒仔細看,還不知道這兩者的差異在哪,就等以後真的遇到專案有這需求時再來研究吧~