Web Speech API 原理與瀏覽器語音合成歷史:由 DECtalk 到神經網絡 TTS
DDEVELOPER
開發發布: 10 分鐘閱讀

Web Speech API 原理與瀏覽器語音合成歷史:由 DECtalk 到神經網絡 TTS

瀏覽器朗讀文字時,會將發話物件交給語音引擎。Web Speech API 本身並不是產生聲音的模型。本文由 Voder、DECtalk 到神經網絡 TTS 的歷史出發,再說明開發網頁時如何選擇語音、了解私隱條件及管理播放狀態。

日文原文發布: 2026-05-13

語音合成的發展:Voder、DECtalk 與 CallText

1939 年紐約世界博覽會上,貝爾實驗室展示了 Homer Dudley 設計的 Voder。操作員透過鍵盤和腳踏控制語音,與今天自動讀出文字的 TTS 系統並不相同。

1980 年代,DECtalk 在 Dennis Klatt 等人的研究基礎上面世,其中 Perfect Paul 以 Klatt 的聲音為基礎。不過,史提芬·霍金使用的裝置是 Speech Plus 的 CallText 5010,而非 DECtalk。兩者與 MITalk 有相關的技術淵源,並不表示它們是同一產品。

產生語音亦有不同方法。以下只是代表性例子,並非將所有合成技術劃分為兩類。

如表格未能完整顯示,可左右捲動。

方法基本原理
共振峰合成以模型描述聲道共振等特性,再產生語音;DECtalk 屬於這一類。
拼接式合成選取已錄製的語音片段並加以拼接,同時處理銜接與語調。

歷史資料參考 Computer History Museum;方法比較參考該館及 Google DeepMind。完整文獻連結列於文末。

作業系統、語音引擎與 API 的分工

桌面環境一直透過 API,讓應用程式使用語音功能。Microsoft 的 Speech API(SAPI)就是其中一例。將 SAPI 稱為「拼接式合成引擎」並不準確:API 是呼叫介面,背後把文字轉成聲音的引擎則是另一層。

Web Speech API 同樣是瀏覽器存取語音合成功能的介面。作業系統已安裝的語音,不一定全部開放給瀏覽器。可用語音、處理位置及提供條件會因瀏覽器、作業系統、設定和服務而異,應以該環境的 getVoices() 結果為準。

Web Speech API 的範圍與支援情況

Web Speech API 同時涵蓋把聲音轉成文字的語音識別,以及把文字轉成聲音的語音合成。本文集中討論後者。識別功能的支援情況及咪高峰權限要求,不應直接套用到合成功能。

2026 年 11 月 12 日核對的規格文件是 Draft Community Group Report,並非 W3C Recommendation。因此,不應將它描述為「2012 年已完成的最終規格」。

Can I use 的語音合成支援表列出 Chrome 33、Safari 7、Firefox 49 及 Edge 14 等起始版本。這些版本是 API 支援歷程的參考,並不保證特定語音、語言、音質或所有功能細節都相同。實際部署前仍須檢查 API、語音清單,並在目標裝置測試。

API 如何運作:建立發話並選擇語音

請將以下 script 放在 HTML 的 <button id="speak" type="button">朗讀</button> 元素之後。按鈕會觸發短句朗讀;找不到符合香港中文/粵語標籤條件的本機語音時,就不播放。範例假設只有這段程式管理頁面的語音佇列。

通常 speak() 會將發話加到佇列。本例先用 cancel() 停止並移除先前的發話,所以在語音可用時再按一次,會開始新的發話。resume() 不會恢復已取消的發話。若最初的清單是空的,請等語音載入後再按一次;後面的清單監聽範例不會自動重試播放。

if ("speechSynthesis" in window && "SpeechSynthesisUtterance" in window) {
  const synth = window.speechSynthesis;
  document.getElementById("speak").addEventListener("click", () => {
    const voice = synth.getVoices().find(v =>
      v.localService && /^(?:zh|yue)-(?:Hant-)?HK$/i.test(v.lang));
    if (!voice) {
      console.log("暫時沒有符合香港語言標籤的本機語音");
      return;
    }
    const utterance = new SpeechSynthesisUtterance("你好,呢個係語音合成示例。");
    utterance.voice = voice;
    utterance.lang = voice.lang;
    utterance.rate = 1;
    utterance.pitch = 1;
    utterance.volume = 1;
    utterance.onend = () => console.log("收到發話結束通知");
    utterance.onerror = event => console.error("朗讀出現錯誤", event.error);
    synth.cancel();
    synth.resume();
    synth.speak(utterance);
  });
} else {
  console.log("這個環境不支援語音合成 API");
}

範例只接受 zh-HK、zh-Hant-HK、yue-HK 或 yue-Hant-HK,不會自動改用其他地區或遙距語音。yue 表示粵語,zh 則是較廣泛的中文標記;單憑 zh-HK 並不能保證實際發音。這是範例的篩選政策,不是完整的語言標籤配對器。供應商可能使用其他標籤,正式產品應列出語音供使用者試聽及選擇。

speak() 要接收發話物件。呼叫完成不等於播放成功,仍須處理錯誤。API 的 rate 範圍是 0.1–10、pitch 是 0–2、volume 是 0–1,但引擎實際支援的範圍與效果可能不同。這個短例沒有實作長文分段、逾時處理或畫面狀態顯示。

語音清單可能稍後才出現

首次呼叫可能得到空陣列,所以要同時處理初次讀取及 voiceschanged 事件。以下獨立範例只記錄清單,不會自動播放。畫面或元件卸載時,應呼叫回傳的函式移除監聽器。若清單持續為空,亦要檢查作業系統的語音設定與瀏覽器提供條件。

function watchLocalVoices(onChange) {
  if (!("speechSynthesis" in window)) return () => {};
  const synth = window.speechSynthesis;
  const update = () => onChange(synth.getVoices().filter(v =>
    v.localService && /^(?:zh|yue)-(?:Hant-)?HK$/i.test(v.lang)));
  synth.addEventListener("voiceschanged", update);
  update();
  return () => synth.removeEventListener("voiceschanged", update);
}
const stopWatching = watchLocalVoices(voices => console.log(voices));
// 畫面或元件卸載時,呼叫 stopWatching()。

發話事件包括 start、end、error、pause 及 resume。boundary 可通知單字或句子的邊界,但各引擎不一定提供相同細緻程度的通知。結束通知亦不能證明使用者實際聽到聲音,或所有發音都正確。

語音差異與私隱

語音種類及音質取決於瀏覽器、作業系統、裝置設定和語音服務。不能單憑瀏覽器名稱就保證某個聲音或音質,應檢查 getVoices() 清單。語音名稱未必唯一;清單更新後,同一陣列位置亦未必對應同一語音。

localService 是瀏覽器回報的處理分類:true 表示本機服務,false 表示遙距服務。遙距服務可能將文字傳送給語音供應商。這個欄位不代表整部裝置或瀏覽器擴充功能都不會進行網絡通訊。

NanToo 的文字朗讀工具(日文介面)預設只顯示本機語音。要使用遙距語音,使用者須取消篩選並明確選取。若已選語音消失,工具不會自動轉用另一語音。工具沒有將輸入文字傳送或儲存到 NanToo 伺服器的處理,但涉及敏感資料時,仍須了解裝置及語音供應商的條件。

神經網絡 TTS:WaveNet 與 Tacotron 2

2016 年發表的 WaveNet 是自迴歸神經網絡,逐一產生原始音訊取樣值。每個新取樣值都以先前產生的值為條件,因此早期方法面對運算成本高的問題。聆聽測試報告的改善,只適用於該實驗的系統與條件,不能推論它優於所有語音合成系統。

Google 在 2017 年 12 月介紹的 Tacotron 2,先由文字字元預測梅爾頻譜圖,再以 WaveNet 類型的聲碼器產生音訊波形。梅爾頻譜圖使用考慮人類聽覺的尺度,表示頻率分布隨時間的變化。這個以字元為輸入的例子,不能描述為「必定從音素序列開始」。

Microsoft 於 2018 年 9 月公布神經網絡 TTS 服務預覽版。不過,研究或雲端產品發表,不表示相關語音一定會出現在某個瀏覽器的 getVoices() 清單。神經網絡 TTS 有不同架構,不能將所有方法限定為固定的三階段流程。

長文朗讀與播放狀態

Chromium 過去曾收到長文朗讀中途停止的問題報告,但這不代表目前所有環境都有共通的 15 秒限制。定時呼叫 pause() 與 resume() 亦不能保證穩定播放。

cancel() 會清空發話佇列,但不會解除暫停狀態;開始新播放時,要用 resume() 解除。每次播放及每個發話物件都應有自己的識別,避免已停止發話的延遲事件被誤認為新播放的事件。錯誤與完成必須分開處理,亦不應無限等待沒有回應的引擎。

NanToo 工具接受最多 5,000 個 UTF-16 碼元,並在不切開字素叢集的前提下,分成每段最多 200 個碼元。輸入過長時,只保留前 5,001 個碼元來偵測超限,不會播放。區段前後的空白會被移除;若單一字素叢集超過 200 個碼元,便拒絕該輸入。每區段上限為 90 秒,整次播放上限為 20 分鐘,暫停時間亦計算在內。背景分頁、鎖定畫面或裝置睡眠可能令偵測與停止延後。更改文字、語音、篩選或設定時會停止播放。

分段不能保證消除語音引擎故障或發音錯誤,區段銜接處亦可能出現不自然的停頓。完成顯示來自引擎通知,並非實際量度音量或檢查發音的結果。

SSML:描述朗讀方式的標記語言

SSML(Speech Synthesis Markup Language)是以 XML 為基礎、用來描述朗讀方式的語言。SSML 1.1 於 2010 年 9 月 7 日成為 W3C Recommendation。以下文件包含 XML 命名空間,但不保證不同瀏覽器產生相同效果。

<speak xmlns="http://www.w3.org/2001/10/synthesis"
       version="1.1" xml:lang="yue-HK">
  你好。
  <break time="500ms"/>
  <prosody rate="slow" pitch="+10%">讀慢啲,音調高少少。</prosody>
</speak>

break 指定停頓,prosody 控制速度、音高等屬性。音素標記方式及 say-as 值的解讀,未必在所有引擎通用。此處的 yue-HK 指定香港粵語,仍須由實際服務支援。

Web Speech API 規格容許發話文字使用純文字或格式良好的 SSML,但實際解讀及支援範圍取決於實作,不能說只有雲端 API 才能處理 SSML。NanToo 工具不接受 SSML/XML 或以角括號包住的表達式,只提供純文字配合 rate、pitch、volume 設定的播放。

使用前要了解的限制

  • 語音及音質:取決於瀏覽器、作業系統、設定及語音供應商。
  • 音訊資料:SpeechSynthesis 沒有將合成結果回傳為 Blob 或音訊串流的標準方法,單純加入 WebCodecs 亦不會讓輸出變得可讀取。
  • SSML 與事件:須按不同實作確認。
  • 長文:分段及逾時是應對措施,不是所有環境都能完整播放的保證。
  • 自訂語音:沒有讓網頁將任意模型註冊到 SpeechSynthesis 語音清單的 API。

以 WebAssembly 或 WebGPU 執行獨立 TTS 模型,與使用瀏覽器提供的 SpeechSynthesis 服務是不同設計。如果目的是朗讀文字,先確認可用語音、處理位置與播放狀態管理;如要產生音訊檔案或使用指定模型,則應考慮支援這些需要的其他合成方法。

參考資料及來源

編輯說明

本文使用 AI 協助編輯,並在發布前由編輯核對。內容仍可能包含事實、理解或時效上的錯誤;作出重要決定前,請查閱所列的一手資料或官方文件。

相關文章