目錄(19 節)

TTS 情緒控制實作:emotion 參數點用(附 API 範例)

廣東話 TTS 嘅情緒控制,係提交任務時加一個 emotion 參數(9 個固定英文值之一)。佢會同 style、instruction 合併成一條演繹指示,次序鎖死 style → emotion → instruction。附可跟住跑嘅 async 提交同 poll 範例。

— 港嘢內容團隊約 9 分鐘ttstutorial
分享:

一句答你:廣東話 TTS 嘅情緒控制,係喺提交生成任務時加一個 emotion 參數(9 個固定英文值之一);佢會同 style、instruction 一齊合併成一條演繹指示,次序係 style → emotion → instruction,所以 instruction 永遠有最終話事權。

情緒控制真實讀音示例

同一句「恭喜晒!你哋嘅努力終於開花結果!」,用平靜聲讀同用興奮聲讀,聽落係兩條片。前者似新聞報道,後者似頒獎典禮。TTS 嘅情緒控制,就係幫你喺提交任務嗰刻,將「點樣讀」同「讀咩」分開嚟設定。

呢篇係實作教學。先講 emotion 係咩、有幾個值,再逐步示範完整 async 提交+poll 流程(可以直接複製去跑),然後拆解 emotion/style/instruction 三者嘅分別同配搭,最後俾你一張「邊種內容配邊種情緒」嘅起點表。想先補基礎,可以睇《粵語 TTS API 快速上手》;想知產品面有咩功能,睇《點樣用 API 生成地道粵語語音》。

Key Takeaways

  • emotion 係提交任務時嘅選填參數,只收 9 個固定英文值:happy/sad/angry/fearful/disgusted/surprised/neutral/excited/gentle
  • 傳錯值會回 400 invalid_emotion — 唔係靜靜當冇填(fail loud,唔會偷偷降級)
  • 三個「演繹類」參數會合併成同一條指示,次序鎖死:style → emotion → instruction;instruction 排最後,最終話事
  • 生成係 async 為主:POST /v1/audio/jobs 回 202 + job_id,再 poll GET /v1/audio/jobs/{id} 到 completed,攞 audio_url 下載
  • ⚠️ Sync 捷徑 POST /v1/audio/speech 而家恆回 503 pool_cold — 可以知有呢條路,但唔好當主路徑
  • 配搭嘅原則係:style 定「點樣講」,emotion 定「咩心情」,instruction 補「你講唔清嘅細節」;三個一齊用唔會撞,只會疊

emotion 參數係咩?9 個值一覽

emotion 描述嘅係講嘢嗰一刻嘅情感狀態 — 係「呢句嘢而家係咩心情」,唔係聲線本身嘅質地,亦唔係角色人設。所以同一把聲、同一段文字,換一個 emotion 就出一個唔同版本。

值中文對應典型場景(起點建議)
happy開心產品介紹、活動預告
excited興奮頒獎、優惠宣布、短片 hook
gentle溫柔客服、睡前故事、品牌關懷
neutral中性新聞、財經、教學、企業培訓
surprised驚訝開箱、劇情、遊戲旁白
sad悲傷劇情、抒情內容、公益呼籲
angry憤怒遊戲角色、衝突劇情
fearful害怕驚悚、遊戲、危機情境
disgusted厭惡角色對白、喜劇效果

三點要注意:

  1. 值係英文 enum,唔係中文。 想用中文情緒詞(例:「雀躍」、「感慨」),唔好塞入 emotion,放去 instruction 嘅自由文字入面。
  2. 係選填。 唔填就唔會加 emotion 呢一段落指示;輸出主要由聲線、style 同文字本身決定。
  3. 值域有硬界。 超出 9 個值就係 400 invalid_emotion,唔會被忽略、唔會被截斷 — 呢個對寫 code 嘅人係好事,錯喺開發階段就會爆,唔會出到街先發現。

如果你用過其他語音系統,呢套「情緒/風格/自由指示」係對應緊業界標準嘅概念。W3C 嘅 Speech Synthesis Markup Language (SSML) 1.1 就係用 <prosody> 控制速率/音高/音量、用 <emphasis> 標示強調,同本 API 嘅 style(節奏、聲底)同理;而 emotion 同 instruction 就相當於「用一句話描述你想要嘅演繹」,唔使自己砌 XML。

完整實作:async 提交 + poll 到完成

呢個係官方文件嘅主路徑:POST 提交 → 202 + job_id → poll → completed → 下載。下面四步可以直接跟住跑。

第 1 步:拎 API key 同聲線 slug

API 入門或以上全速開放(免費方案有 Sandbox 模式,每日 50 個生成請求,可以試接);key 用 Authorization: Bearer 傳,前綴 sk_live_。key 嘅查看/管理規則以 API keys 頁面為準,唔見咗就 revoke 再開一把新。

export CANTOGEN_API_KEY="sk_live_…"

curl -s https://canto-gen.com/v1/voices \
  -H "Authorization: Bearer $CANTOGEN_API_KEY"

聲線用 slug(ASCII 穩定 id),唔係中文名。例:sum = 心妍,female,adult。永遠由你自己 GET /v1/voices 嘅回應揀 slug,唔好照抄文件範例。

想知某把聲支援咩演繹風格,可以問 GET /v1/voices/{slug}/styles。

第 2 步:提交 job(帶 emotion)

curl -s -X POST https://canto-gen.com/v1/audio/jobs \
  -H "Authorization: Bearer $CANTOGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "cantogen-tts",
    "voice": "sum",
    "text": "恭喜晒!你哋嘅努力終於開花結果!",
    "emotion": "excited",
    "style": "brisk",
    "instruction": "講快啲,尾音上揚,好似現場宣布咁",
    "response_format": "mp3"
  }'

成功會回 202 Accepted:

{
  "job_id": "b7e6f2a1-4c3d-4e5f-8a9b-0c1d2e3f4a5b",
  "status": "queued",
  "poll_url": "/v1/audio/jobs/b7e6f2a1-4c3d-4e5f-8a9b-0c1d2e3f4a5b",
  "srt_requested": false
}

model 可以省略,"cantogen-tts" 係唯一值。文字上限 500 字,超出回 400 text_too_long,唔會靜靜截斷。

第 3 步:poll 到 completed

狀態生命周期:queued → processing → completed|failed|cancelled。建議每隔幾秒 poll 一次,帶少少 backoff。

JOB_ID="b7e6f2a1-4c3d-4e5f-8a9b-0c1d2e3f4a5b"

curl -s "https://canto-gen.com/v1/audio/jobs/$JOB_ID" \
  -H "Authorization: Bearer $CANTOGEN_API_KEY"

想寫入程式,呢段 Node 範例係一個實用嘅 poll loop(2 秒起步、每次放大 1.5 倍、最多等 5 秒一次):

async function pollJob(jobId) {
  let delayMs = 2000;
  for (let attempt = 0; attempt < 60; attempt++) {
    const res = await fetch(`https://canto-gen.com/v1/audio/jobs/${jobId}`, {
      headers: { Authorization: `Bearer ${process.env.CANTOGEN_API_KEY}` },
    });
    const job = await res.json();

    if (job.status === "completed") return job;           // 有 audio_url
    if (job.status === "failed" || job.status === "cancelled") {
      throw new Error(`job ${jobId} ended as ${job.status}`);
    }
    await new Promise((r) => setTimeout(r, delayMs));
    delayMs = Math.min(delayMs * 1.5, 5000);
  }
  throw new Error("poll timeout");
}

const job = await pollJob("b7e6f2a1-4c3d-4e5f-8a9b-0c1d2e3f4a5b");
console.log(job.audio_url);

第 4 步:攞 audio_url 下載

completed 之後回應會多一個 audio_url:

{
  "job_id": "b7e6f2a1-4c3d-4e5f-8a9b-0c1d2e3f4a5b",
  "status": "completed",
  "poll_url": "/v1/audio/jobs/b7e6f2a1-4c3d-4e5f-8a9b-0c1d2e3f4a5b",
  "srt_requested": false,
  "audio_url": "https://canto-gen.com/storage/v1/object/sign/audio_history/…"
}

呢條係預簽名 URL,下載唔使再帶 Authorization:

curl -sL -o excited.mp3 "$AUDIO_URL"

⚠️ audio_url 有期限。過期唔等於要重新生成 — 再 poll 一次同一個 job,會攞到新簽嘅 URL。所以做法係:即刻下載,或將 job_id 存起,需要時重新 poll。

關於 sync 捷徑(現況必讀)

POST /v1/audio/speech 係短文本同步捷徑:≤300 字符、只支援 voice/design 模式,直接回 200 + audio bytes。聽落方便,但而家恆回 503 pool_cold:

HTTP/1.1 503 Service Unavailable

{ "error": "pool_cold", "hint": "use POST /v1/audio/jobs (async)" }

而且佢係 fail closed — 唔會偷偷幫你轉行 async。所以寫 code 嘅時候,主路徑一律用 /v1/audio/jobs;sync 可以當「將來可能有」嘅優化,唔好當 now 嘅方案。另外 clone 同 srt: true 本身都唔支援 sync 軌(分別回 400 clone_not_supported_sync / 400 srt_not_supported_sync)。

emotion vs style vs instruction:分別同配搭

三個參數都係「演繹類」,但分工唔同。佢哋最後會合併成同一條演繹指示,以逗號串接,次序鎖死:style → emotion → instruction。

參數係咩值域合併次序幾時用
style演繹風格:節奏、音色、場景、角色canonical 英文 slug(中文 preset 別名都收);亦可傳自由文字最先定「點樣講」— 快慢、起伏、聲底
emotion講嘢嗰刻嘅情感狀態9 個固定英文 enum中間定「咩心情」— 要穩定、可預期
instruction自由文字演繹指示(廣東話/中文/英文都得)自由文字,最多 500 字最後(最終話事)補細節 — 你講唔清、但聽得出嘅位

幾條實用規則:

  • style 同 emotion 可以一齊用,唔會互相覆蓋,會疊埋一齊。
  • instruction 排最後,所以佢贏。 如果你 emotion: "gentle" 但 instruction 寫「用興奮嘅語氣講」,聽落會偏興奮 — 因為指示疊到最後。呢個係設計,唔係 bug:佢俾你「用 preset 打底、再用文字微調」。
  • style 中文別名都收。 英文 slug 係 canonical,但原本嘅中文 preset 值照收,內部會正規化到同一個值。
  • instruction 超出 500 字回 400 invalid_instruction,唔會截斷。
  • 三個演繹參數同 voice/design/clone 三個生成模式互斥:模式一次只可以送一個,否則 400 voice_mode_conflict。

一句總結:style = 點樣講;emotion = 咩心情;instruction = 你講唔清嘅嗰啲細節。

實用配搭建議:邊種內容配邊種情緒

下表係起點建議(唔係實測最佳組合)。做法係先由內容類型揀一個 emotion 打底,再用 style 定節奏,最後有需要先加 instruction。

內容類型emotion 起點style 起點instruction 例子
產品廣告/優惠宣布excitedbrisk「尾句上揚,好似現場宣布咁」
新聞/財經旁白neutralsteady「保持平穩,唔好加情緒起伏」
教學/教材neutral 或 gentleslow「慢半拍,每句尾停一停」
有聲書/講古gentle(悲情段可 sad)drawl「娓娓道來,句與句之間留白」
企業形象/培訓neutralcomposed「沉穩自信,唔急」
短片 hookexcited 或 surprisedrapid「頭三個字加快,製造懸念」
客服/提示音gentlesteady「親切啲,語速放慢」
遊戲角色對白angry 或 fearfulsharp「咬牙切齒,聲線壓低」

上表係按參數語意同常見配音慣例砌出嘅起點,未做過 A/B 實測。真正邊個組合好聽,要你攞自己嘅稿實測;情緒效果冇「一定啱」嘅組合,只有「你條片啱用」嘅組合。

配套兩篇可以一齊睇:語氣詞(「喇」、「囉」、「㗎」)嘅情緒落點,見《粵語語氣詞 AI 讀法》;要出字幕就見《TTS SRT 字幕工作流》。

常見錯誤同排查

情況你見到點解/點做
emotion 打錯字400 invalid_emotion只收 9 個值之一;對一次 spell 再送
instruction 太長400 invalid_instruction上限 500 字;精簡重點,唔好貼成段稿
同時送 voice 同 design400 voice_mode_conflict三個模式互斥,一次只送一個
文字超長400 text_too_long上限 500 字,唔會截斷;拆句提交
短時間送太多429 rate_limit_exceeded預設 60/min per key;跟 Retry-After 退避
月配額用完403 monthly_cap_reached等 UTC 月份重置,或調高 key 上限
Poll 一個唔存在嘅 id404 job_not_foundid 可能錯、或屬另一條 key
撞到 503jobs_unavailable 類屬暫時性後端問題,backoff retry
用咗 sync endpoint503 pool_cold改用 POST /v1/audio/jobs(async)

兩個小提醒:

  • 讀取 endpoint 唔受 rate limit(GET /v1/voices、GET /v1/audio/jobs/{id}),所以 poll 唔會食你嘅 per-minute 額度。
  • API 用量同 playground 共用同一份月配額,試音同正式生成係同一池。

想一次試幾個情緒版本,可以並行提交幾個 job(每個 emotion 一個)再一齊 poll;要大量試就見《TTS 批量生成工作流》。想睇唔同平台喺粵語情緒演繹上嘅分別,見《廣東話 TTS 2026 對比》。

常見問題 FAQ

emotion 同 style 可以一齊用嗎?邊個行先?

可以,兩個都係選填,而且會疊埋一齊。合併次序係 style → emotion → instruction,所以你兩個都送,兩個都會生效;最後由 instruction 話事。

唔填 emotion 會點?有冇預設值?

emotion 係選填。唔填就唔會加入 emotion 呢一段指示,輸出主要由你揀嘅聲線、style 同文字本身決定。想有明確情緒,就明寫一個值。

我想用中文情緒詞(例:「雀躍」、「感慨」),可以放入 emotion 嗎?

唔可以。emotion 只收 9 個固定英文 enum(happy/sad/angry/fearful/disgusted/surprised/neutral/excited/gentle),其他值一律 400 invalid_emotion。中文情緒描述請放去 instruction 嘅自由文字。

instruction 同 style 一齊用,邊個話事?

instruction。合併次序鎖死係 style → emotion → instruction,排最後嘅有最終話事權。呢個設計令你可以「用 style/emotion 打底,再用 instruction 微調」。記住 instruction 上限 500 字,超出會回 400 invalid_instruction。

點解官方文件有 sync endpoint,但你叫我用 async?

因為 POST /v1/audio/speech 而家恆回 503 pool_cold,而且係 fail closed(唔會偷偷轉 async)。sync 語法上係短文本捷徑(≤300 字符、只支援 voice/design),但現階段唔可以當主路徑。用 POST /v1/audio/jobs + poll 就穩陣。

Job 生成緊,可唔可以中途改 emotion?

唔可以。參數喺提交嗰刻就鎖定。要改就要取消原本個 job(DELETE /v1/audio/jobs/{id})再重新提交一個。取消嘅可用性同計費處理,請以提交時嘅文件同實際回應為準。

同一段稿想試齊幾個情緒,點做最有效率?

並行提交幾個 job,每個一個 emotion,然後一齊 poll。因為 poll 讀取唔受 per-minute rate limit,可以放心輪詢;提交就留意預設 60/min 嘅限制,加少少 backoff 就好。

由一句「試下唔同版本」開始

情緒控制唔係「揀一個最靚嘅值」,而係一個可比較嘅實驗:同一句稿、同一把聲,只換 emotion,聽齊幾個版本,你就即刻知邊個啱你條片。

情緒係演繹嘅一半,另一半係讀音;香港人講嘢,兩樣都值得認真對待。


相關文章