◉GEMINI LABEN
●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…
記事一覧/開発ツール
⟐ 開発ツール/2026-07-03上級

音声が全部できるまで待たせない — Gemini TTS ストリーミング生成を配信経路に組み込む設計

gemini-3.1-flash-tts-preview が streamGenerateContent に対応しました。最初の音が出るまで実測1.8秒の配信経路を、PCM境界処理・文単位の再開・preview停止フォールバックまで含めて設計します。

Gemini API248TTS4ストリーミング13音声生成3FastAPI

✦ プレミアム記事

個人開発で運営している Dolice Labs のサイト群で、記事を音声でも聴けるようにする実験を続けております。

これまでのボトルネックは生成そのものではなく「待ち時間」でした。3,800字ほどの原稿をバッチの TTS に渡すと、音声ファイルが完成するまで平均41秒。ポッドキャストのように作り置きする用途なら問題になりませんが、ページ上の「読み上げボタン」を押した読者を41秒待たせるのは現実的ではありません。

2026年7月の変更で、この前提が変わりました。gemini-3.1-flash-tts-preview が streamGenerateContent 経由のストリーミング音声生成に対応し、音声を「作り終えてから配る」のではなく「作りながら配る」経路を組めるようになりました(Gemini API changelog)。

実際に配信経路を作り直してみると、SDK の呼び出し自体は簡単な一方で、その先の「配り方」にいくつも設計判断が必要でした。ここからは、その過程で確定した構成をコードと実測値でまとめておきます。

「作ってから配る」と「作りながら配る」で何が変わるか

同じ TTS でも、バッチとストリーミングでは設計の重心が別物です。最初にここを整理しておくと、後の判断がぶれません。

観点バッチ生成(作ってから配る)ストリーミング生成(作りながら配る)
最初の音までの時間原稿全体の生成完了まで(実測41秒/3,800字)先頭チャンク到着まで(実測1.8秒)
成果物完成したファイル(WAV/MP3)PCM チャンクの列。ファイルは後から組み立てる
失敗時の意味論最初から再生成すればよい(冪等)途中まで再生済み。どこから再開するかの設計が必要
向く用途ポッドキャスト・動画ナレーション・作り置き読み上げボタン・対話 UI・その場で聴かせる導線

私の結論を先に書くと、アーカイブ用の音声はバッチのまま残し、「その場で聴かせる」導線だけをストリーミングに切り替えました。両方をストリーミングに寄せる必要はありません。

受け口 — streamGenerateContent から PCM チャンクを取り出す

サーバー側の受け口はこれだけです。ポイントは、チャンクから取り出せるのが 24kHz・16bit・モノラルの生 PCM だという点です。

# tts_stream.py — ストリーミング TTS の受け口
from google import genai
from google.genai import types
 
client = genai.Client()  # GEMINI_API_KEY を環境変数から読み込みます
 
TTS_MODEL = "gemini-3.1-flash-tts-preview"  # 後述の理由で必ず設定に外出しします
 
def stream_tts(text: str):
    """テキストを音声チャンク(24kHz 16bit mono PCM)のジェネレータに変換します。"""
    stream = client.models.generate_content_stream(
        model=TTS_MODEL,
        contents=text,
        config=types.GenerateContentConfig(
            response_modalities=["AUDIO"],
            speech_config=types.SpeechConfig(
                voice_config=types.VoiceConfig(
                    prebuilt_voice_config=types.PrebuiltVoiceConfig(
                        voice_name="Kore"
                    )
                )
            ),
        ),
    )
    for chunk in stream:
        if not chunk.candidates:
            continue
        part = chunk.candidates[0].content.parts[0]
        if part.inline_data and part.inline_data.data:
            yield part.inline_data.data

これを HTTP に載せます。FastAPI ならチャンク転送そのままです。

# server.py — チャンク転送で配る
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from tts_stream import stream_tts
 
app = FastAPI()
 
@app.get("/tts")
def tts(text: str):
    return StreamingResponse(
        stream_tts(text),
        media_type="audio/L16;rate=24000;channels=1",
        headers={"Cache-Control": "no-store"},
    )

Content-Type を audio/L16 にしているのは意図的です。ここで「なぜ普通に WAV で返さないのか」という問題に突き当たります。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦streamGenerateContent の音声チャンクを FastAPI からチャンク転送で配る最小構成と、最初の音が出るまで実測1.8秒(バッチ比 約23分の1)のレイテンシ設計
✦長さ未確定のまま WAV を配れない問題への3つの選択肢の比較と、生PCM+クライアント再生に落ち着いた判断根拠(Int16境界の持ち越しバッファ実装つき)
✦途中切断をバイト位置ではなく文境界で再開する chunk map 設計と、preview モデル停止に備えたバッチへの自動フォールバック
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

◈ API / SDK2026-08-23
Gemini TTS を streamGenerateContent で受け取り、先頭のチャンクから鳴らす
Gemini の TTS をストリーミングで受け取ると、音声は「ファイル」ではなく PCM の断片で届きます。チャンクごとに WAV ヘッダを付けた場合に何が起きるかを手元で測り、連結とヘッダの正しい順序を実装まで示します。
⟐ 開発ツール2026-09-10
浮世絵カタログの表記ゆれ照合を、正規化と候補列挙の二層で組む手順
同じ絵師名が三通りに散らばったカタログを揃えるとき、Gemini に同一判定をさせると世代違いが混ざります。正規化を先に置き、モデルには候補の列挙だけを頼む二層構成を、動くコードと費用の見積もりつきでお伝えします。
⟐ 開発ツール2026-09-06
アイコンだけのボタンは、翻訳が揃っていても読み上げでは無言になります
翻訳ファイルが揃っていても、読み上げラベルの穴は残ります。iOS と Android からアイコン要素とラベルを集め、機械で決まる不備をローカルで確定させてから Gemini に文言だけを渡す二段構えを、実際の出力とともに書き残します。