Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.supertoneapi.com/llms.txt

Use this file to discover all available pages before exploring further.

このドキュメントは英語の原文から自動翻訳されています。表現に不自然な箇所がある場合があります。正確な内容は英語の原文もあわせてご確認ください。
生成音声にキャラクターをリップシンクさせるには、次の3つの情報を同期して扱う必要があります。
  1. オーディオファイル
  2. 実際に発話された音素(phoneme)シンボル
  3. 各音素の開始時刻と継続時間
TTSリクエストでinclude_phonemes: trueを指定すると、Supertoneはこの3つすべてを返します。

Python — オーディオと音素をリクエストする

import base64
import os
from supertone import Supertone

VOICE_ID = "20160a4c5ba38967330c84"  # replace with your voice ID

with Supertone(api_key=os.environ["SUPERTONE_API_KEY"]) as client:
    response = client.text_to_speech.create_speech(
        voice_id=VOICE_ID,
        text="Welcome to the workshop.",
        language="en",
        model="sona_speech_2",
        include_phonemes=True,
    )

    audio_bytes = base64.b64decode(response.result.audio_base64)
    with open("speech.wav", "wb") as f:
        f.write(audio_bytes)

    phonemes = response.result.phonemes
    for symbol, start, duration in zip(
        phonemes.symbols,
        phonemes.start_times_seconds,
        phonemes.durations_seconds,
    ):
        print(f"{start:7.3f}s  {duration:5.3f}s  {symbol!r}")

TypeScript — オーディオと音素をリクエストする

import { Supertone } from "@supertone/supertone";
import * as fs from "node:fs";

const VOICE_ID = "20160a4c5ba38967330c84"; // replace with your voice ID

const client = new Supertone({ apiKey: process.env.SUPERTONE_API_KEY });

const response = await client.textToSpeech.createSpeech({
  voiceId: VOICE_ID,
  apiConvertTextToSpeechUsingCharacterRequest: {
    text: "Welcome to the workshop.",
    language: "en",
    model: "sona_speech_2",
    includePhonemes: true,
  },
});

const result = response.result as {
  audioBase64: string;
  phonemes?: { symbols?: string[]; startTimesSeconds?: number[]; durationsSeconds?: number[] };
};

fs.writeFileSync("speech.wav", Buffer.from(result.audioBase64, "base64"));

const { symbols = [], startTimesSeconds = [], durationsSeconds = [] } = result.phonemes ?? {};
for (let i = 0; i < symbols.length; i++) {
  console.log(
    `${startTimesSeconds[i].toFixed(3)}s  ${durationsSeconds[i].toFixed(3)}s  ${symbols[i]}`,
  );
}

音素をビゼーム(viseme)にマッピングする

よく使われる描画パイプラインでは、各IPA形式のシンボルを少数の口形(ビゼーム)にマッピングし、それらを補間しながら3Dリグや2Dスプライトを駆動します。
// Minimal English IPA → viseme mapping (extend for your rig)
const PHONEME_TO_VISEME: Record<string, string> = {
  // Closed lip
  "p": "BMP",
  "b": "BMP",
  "m": "BMP",
  // Open vowel
  "ɑ": "Aa",
  "ʌ": "Aa",
  "ɐ": "Aa",
  // Wide smile
  "iː": "Ee",
  "i": "Ee",
  // Rounded
  "uː": "Oo",
  "u": "Oo",
  "o": "Oo",
  // Fricative
  "f": "FV",
  "v": "FV",
  "θ": "Th",
  // Silence
  "": "Rest",
};

interface VisemeKeyframe {
  time: number;
  duration: number;
  viseme: string;
}

function buildVisemeTrack(
  symbols: string[],
  starts: number[],
  durations: number[],
): VisemeKeyframe[] {
  return symbols.map((symbol, i) => ({
    time: starts[i],
    duration: durations[i],
    viseme: PHONEME_TO_VISEME[symbol] ?? "Rest",
  }));
}
レンダーループでは、現在のオーディオ時刻を進めながら、そのタイムスタンプに該当するビゼームを参照します。ビゼームのウェイトをトゥイーンさせることで、口形がカクッと切り替わらないようにしましょう。

音素をリアルタイムでストリーミングする

stream_speechinclude_phonemes: true付きで呼び出すと、レスポンスはNDJSONになります。受信した各行をパースしながら、リアルタイムでリップシンクを駆動できます。
import json

response = client.text_to_speech.stream_speech(
    voice_id=VOICE_ID,
    text="Streaming lip sync in real time.",
    language="en",
    model="sona_speech_1",
    include_phonemes=True,
)

for line in response.result.iter_lines():
    if not line:
        continue
    payload = json.loads(line)
    audio_chunk = base64.b64decode(payload["audio_base64"])
    schedule_audio(audio_chunk)
    schedule_phonemes(payload["phonemes"])

ヒント

  • 音素をサポートするモデルを使ってください。 sona_speech_2sona_speech_2_flashsona_speech_1はいずれも音素をサポートします。supertonic_api_3supertonic_api_1はサポートしません。
  • 滑らかな遷移。 実際の口は形を瞬時に切り替えません。多くのエンジンでは50〜80ms程度かけてビゼームのウェイトを補間します。APIから返る音素の継続時間は、こうしたトゥイーンの良い出発点になります。
  • 強勢と間。 symbolの値が空のものは無音や間を表します — その間は口を休止ポーズに戻してください。
  • マッピングをローカライズしましょう。 音素からビゼームへの対応表は言語ごとに異なります。多言語コンテンツを提供する場合は、韓国語と日本語に合わせてマッピングを調整してください。

関連情報

発音と音素

include_phonemesとレスポンス形式のリファレンスです。

ストリーミング音声合成

リアルタイムリップシンク向けのNDJSONストリーミングです。