使用指南

Tts

目錄

  1. 概述
  2. 查詢可用語音
  3. 語音試聽
  4. 即時 TTS(WebSocket)
  5. 歷史 TTS(SSE)
  6. 廣播 TTS
  7. Word Boundary 卡拉 OK 效果
  8. TTS 設定管理
  9. 相關 Reference 文件

概述

VAS 提供 TTS(Text-to-Speech)語音合成功能,可將翻譯後的文字轉換為語音播放。系統支援 154 種語言,共 325 種語音可供選擇(與語音服務供應商的 Monolingual Neural Voice 完全對齊)。

支援語言

語言代碼語言名稱語音數量
zh-TW繁體中文3
zh-CN簡體中文4
en-US英語(美國)6
en-GB英語(英國)3
ja-JP日語4
ko-KR韓語4
fr-FR法語3
de-DE德語3
es-ES西班牙語3
it-IT義大利語3
pt-BR葡萄牙語(巴西)3
th-TH泰語3
vi-VN越南語2
id-ID印尼語2

上表為熱門 locale 摘要(共 14 個 locale、46 個語音)。完整 154 個 locale × 325 個語音請以 GET /api/v1/tts/voices?language={code} 查詢為準。

核心特色

  • 多場景支援:即時 TTS(WebSocket)、歷史 TTS(SSE)、廣播 TTS 三種場景
  • Word Boundary:每個字詞附帶精確的時間戳記,支援卡拉 OK 逐字高亮效果
  • 同步/非同步模式:sync 模式自動播放最新翻譯,async 模式手動控制播放
  • 多語言廣播 TTS:廣播模式可為不同翻譯語言分別設定 TTS 語音

認證方式

所有 TTS 相關的 REST API 需透過 API Key 認證。詳見 認證說明


查詢可用語音

在使用 TTS 之前,先查詢指定語言有哪些可用語音。

支援語言

VAS 目前支援 154 種語言的 TTS 語音合成,完整清單請參考附錄 - 支援語言

取得指定語言的語音列表

GET https://vas-poc.vurbo.ai/api/v1/tts/voices?language={language}

範例:查詢英文語音

curl -X GET "https://vas-poc.vurbo.ai/api/v1/tts/voices?language=en-US" \
  -H "X-API-Key: YOUR_API_KEY"

回應重點欄位:

欄位說明
voice_name語音識別碼,用於 API 呼叫
display_name語音顯示名稱
gender性別:Female / Male
is_default是否為該語言的預設語音
sample_url試聽音訊 URL

完整參數與回應格式請參考 TTS REST API


語音試聽

查詢語音列表後,可使用 sample URL 試聽各語音的效果。

GET https://vas-poc.vurbo.ai/api/v1/tts/voices/{voiceName}/sample

重點:

  • 回應為 MP3 音訊二進位資料(非 JSON)
  • 首次請求會即時合成並快取,後續請求直接從快取返回
  • 不計入 TTS 費用
  • 限流:每分鐘 30 次/每用戶

前端試聽範例:

// 直接使用 Audio 元素播放
const audio = new Audio(
  'https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-JennyNeural/sample'
);
audio.play();

即時 TTS(WebSocket)

即時 TTS 在語音辨識進行中,即時將翻譯結果轉為語音。透過 WebSocket 發送與接收。

啟用 TTS

start action 中加入 TTS 相關參數:

{
  "type": "voice-translation",
  "data": {
    "action": "start",
    "transcription_languages": ["zh-TW"],
    "translation_languages": ["en-US"],
    "realtime_translation": true,
    "type": "transcribe",
    "tts_enabled": true,
    "tts_language": "en-US",
    "tts_voice": "en-US-JennyNeural",
    "tts_mode": "sync"
  }
}
參數說明
tts_enabled設為 true 啟用 TTS
tts_languageTTS 輸出語言(必須在 translation_languages 中)
tts_voiceTTS 語音名稱(如 en-US-JennyNeural
tts_modesync(同步,自動播放)或 async(非同步,手動控制)

同步模式(sync)

  • 系統自動播放最新的 is_final=true 翻譯句子
  • 若前一句仍在播放,後續句子進入佇列等待
  • 適合不需手動控制的場景

非同步模式(async)

用戶可手動選擇任意已翻譯的句子進行 TTS 播放。支援對同一個 sid 重複請求(重播)。

**互譯模式(conversation)**同樣支援 tts_mode: "async"。設定後翻譯完成時不會自動推送 tts_ready,需透過 tts_play 手動觸發。互譯模式下會自動根據 tts_config 合成對應語言。

播放指定句子:

{
  "type": "voice-translation",
  "data": {
    "action": "tts_play",
    "sid": 5
  }
}

播放多句(從 sid 5 開始播放 3 句):

{
  "type": "voice-translation",
  "data": {
    "action": "tts_play",
    "sid": 5,
    "length": 3
  }
}

length 最大值為 20(由後端 TTS_SSE_MAX_LENGTH 控制)。

停止播放:

{
  "type": "voice-translation",
  "data": {
    "action": "tts_stop"
  }
}

接收 TTS 音訊

TTS 合成完成時,伺服器推送 tts_ready 事件:

{
  "type": "voice-translation",
  "data": {
    "action": "tts_ready",
    "sid": 1,
    "language": "en-US",
    "transcript": "你好,很高興認識你",
    "text": "Hello, nice to meet you",
    "audio": "Base64EncodedMP3...",
    "format": "mp3",
    "duration_ms": 2500,
    "boundaries": [
      {"offset_ms": 0, "duration_ms": 350, "text_offset": 0, "word_length": 5, "text": "Hello"},
      {"offset_ms": 500, "duration_ms": 250, "text_offset": 7, "word_length": 4, "text": "nice"},
      {"offset_ms": 750, "duration_ms": 200, "text_offset": 12, "word_length": 2, "text": "to"},
      {"offset_ms": 950, "duration_ms": 350, "text_offset": 15, "word_length": 4, "text": "meet"},
      {"offset_ms": 1300, "duration_ms": 300, "text_offset": 20, "word_length": 3, "text": "you"}
    ]
  }
}

前端播放範例:

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.data?.action === 'tts_ready') {
    const { audio, boundaries, text } = msg.data;

    // Base64 轉 Blob
    const byteChars = atob(audio);
    const byteArray = new Uint8Array(byteChars.length);
    for (let i = 0; i < byteChars.length; i++) {
      byteArray[i] = byteChars.charCodeAt(i);
    }
    const blob = new Blob([byteArray], { type: 'audio/mp3' });
    const audioEl = new Audio(URL.createObjectURL(blob));
    audioEl.play();
  }
};

切換 TTS 模式

錄音進行中可動態切換同步/非同步模式:

{
  "type": "voice-translation",
  "data": {
    "action": "tts_mode",
    "tts_mode": "async"
  }
}

歷史 TTS(SSE)

歷史 TTS 用於播放已完成錄音的翻譯語音,透過 SSE 串流逐句發送音訊。

請求格式

GET https://vas-poc.vurbo.ai/api/v1/sse/tts/{taskId}?language={language}&sid={sid}&length={length}
參數必填說明
taskId錄音 ID
languageTTS 輸出語言(如 en-US
voice指定語音名稱(如 en-US-JennyNeural
sid起始句子 ID(預設 1)
length回傳句子數量(預設 1,最大 20)

事件序列

connected  ->  tts_audio (重複 N 次)  ->  tts_done
  1. connected:連線確認,包含語音資訊
  2. tts_audio:逐句發送 TTS 音訊(含 Word Boundary)
  3. tts_done:所有句子發送完成

多句播放範例

async function playTTS(taskId, language, apiKey, startSid = 1, length = 3) {
  const url = new URL(`https://vas-poc.vurbo.ai/api/v1/sse/tts/${taskId}`);
  url.searchParams.set('language', language);
  url.searchParams.set('sid', startSid);
  url.searchParams.set('length', length);

  const response = await fetch(url, {
    headers: { 'X-API-Key': apiKey }
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const events = parseSSE(decoder.decode(value));
    for (const event of events) {
      if (event.type === 'tts_audio') {
        // 播放音訊並設定卡拉 OK 效果
        const blob = base64ToBlob(event.data.audio, 'audio/mp3');
        const audio = new Audio(URL.createObjectURL(blob));
        setupKaraoke(audio, event.data.boundaries, event.data.text);
        audio.play();
      }
    }
  }
}

注意:瀏覽器原生 EventSource 不支援自訂 Header,需使用 fetch API 搭配 ReadableStream。


廣播 TTS

廣播模式的 TTS 讓觀眾端可以接收翻譯語音。TTS 音訊透過 SSE 推送給觀眾。

主講者端設定

在建立廣播(REST API)或啟動 WebSocket 時,透過 tts_config 指定哪些語言啟用 TTS:

建立廣播時設定(REST API):

curl -X POST "https://vas-poc.vurbo.ai/api/v1/broadcasts" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transcription_language": "zh-TW",
    "translation_languages": ["en-US", "ja-JP"],
    "tts_config": {
      "en-US": {"voice": "en-US-JennyNeural", "speaking_rate": 1.0},
      "ja-JP": {"voice": "ja-JP-NanamiNeural", "speaking_rate": 1.0}
    }
  }'

WebSocket start 時設定:

{
  "type": "voice-translation",
  "data": {
    "action": "start",
    "type": "broadcast",
    "broadcast_token": "YOUR_BROADCAST_TOKEN",
    "audio_format": "pcm",
    "tts_config": {
      "en-US": {
        "voice": "en-US-JennyNeural",
        "speaking_rate": 1.0
      },
      "ja-JP": {
        "voice": "ja-JP-NanamiNeural",
        "speaking_rate": 1.0
      }
    }
  }
}

tts_config 參數

欄位類型說明
voicestringTTS 語音名稱
speaking_ratenumber語速(0.5 ~ 2.0,預設 1.0)

觀眾端接收 TTS

觀眾連線 SSE 時加入 tts=true 參數:

const eventSource = new EventSource(
  'https://vas-poc.vurbo.ai/broadcast/{token}/text?lang=en-US&tts=true'
);

eventSource.addEventListener('tts_ready', (e) => {
  const data = JSON.parse(e.data);
  // data.audio 為 Base64 編碼的 MP3
  // data.boundaries 為 Word Boundary 陣列
  const blob = base64ToBlob(data.audio, 'audio/mp3');
  const audio = new Audio(URL.createObjectURL(blob));
  audio.play();
});

重要注意事項

  • TTS 語言必須在 translation_languages 中,無效語言會被自動忽略
  • 主講者(WebSocket)不會收到 TTS 音訊,只有 SSE 觀眾會收到 tts_ready 事件
  • TTS 只在 live 階段發送,standby 預備階段不會發送

Word Boundary 卡拉 OK 效果

TTS 回應中包含 boundaries 陣列,記錄每個字詞在音訊中的精確時間位置。利用此資訊可實作卡拉 OK 逐字高亮效果。

Word Boundary 資料結構

欄位類型說明
offset_msint該字詞在音訊中的起始時間(毫秒)
duration_msint該字詞持續時間(毫秒)
text_offsetint在文字字串中的起始位置(字元索引)
word_lengthint字詞長度(字元數)
textstring字詞內容

範例資料

以 "Hello, nice to meet you" 為例:

[
  {"offset_ms": 0,    "duration_ms": 350, "text_offset": 0,  "word_length": 5, "text": "Hello"},
  {"offset_ms": 350,  "duration_ms": 100, "text_offset": 5,  "word_length": 1, "text": ","},
  {"offset_ms": 500,  "duration_ms": 250, "text_offset": 7,  "word_length": 4, "text": "nice"},
  {"offset_ms": 750,  "duration_ms": 200, "text_offset": 12, "word_length": 2, "text": "to"},
  {"offset_ms": 950,  "duration_ms": 350, "text_offset": 15, "word_length": 4, "text": "meet"},
  {"offset_ms": 1300, "duration_ms": 300, "text_offset": 20, "word_length": 3, "text": "you"}
]

實作卡拉 OK 效果

function setupKaraoke(audioElement, boundaries, text) {
  const updateHighlight = () => {
    const currentTimeMs = audioElement.currentTime * 1000;

    // 找到目前正在播放的字詞
    const currentWord = boundaries.find((b, i) => {
      const nextOffset = boundaries[i + 1]?.offset_ms ?? Infinity;
      return currentTimeMs >= b.offset_ms && currentTimeMs < nextOffset;
    });

    if (currentWord) {
      // 高亮當前字詞
      highlightWord(text, currentWord.text_offset, currentWord.word_length);
    }
  };

  // 每 50ms 更新高亮位置
  const interval = setInterval(updateHighlight, 50);
  audioElement.addEventListener('ended', () => clearInterval(interval));
}

function highlightWord(text, offset, length) {
  const before = text.substring(0, offset);
  const word = text.substring(offset, offset + length);
  const after = text.substring(offset + length);

  // 更新 DOM(依實際 UI 框架調整)
  document.getElementById('tts-text').innerHTML =
    `${before}<span class="highlight">${word}</span>${after}`;
}

CSS 樣式參考

.highlight {
  background-color: #FFD700;
  color: #000;
  padding: 2px 4px;
  border-radius: 3px;
  transition: background-color 0.1s ease;
}

TTS 設定管理

切換 TTS 模式

錄音進行中可隨時切換同步/非同步模式:

{
  "type": "voice-translation",
  "data": {
    "action": "tts_mode",
    "tts_mode": "async"
  }
}

成功回應:

{
  "type": "voice-translation",
  "data": {
    "action": "tts_mode_changed",
    "tts_mode": "async"
  }
}

廣播模式動態更新 TTS 設定

廣播進行中可透過 REST API 更新 TTS 設定:

curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/{id}" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tts_config": {
      "zh-TW": {"voice": "zh-TW-HsiaoChenNeural", "speaking_rate": 1.0},
      "ja-JP": {"voice": "ja-JP-NanamiNeural", "speaking_rate": 1.2}
    }
  }'

清除 TTS 設定(傳入 null):

curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/{id}" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tts_config": null
  }'

TTS 錯誤處理

錯誤碼說明處理建議
tts_not_enabledTTS 未啟用在 start 時啟用 TTS
tts_segment_not_found找不到指定句子確認 SID 存在
tts_translation_not_found缺少該語言的翻譯確認翻譯存在
translation_not_found找不到翻譯確認翻譯已完成
tts_synthesis_failedTTS 合成失敗稍後重試
tts_quota_exceededTTS 使用量已達上限稍後重試
invalid_data無效的模式使用 syncasync

相關 Reference 文件


版本:V1.5.7 最後更新:2026-05-20

Copyright © 2026