การสร้างการอ่านออกเสียงข้อความ (TTS)

Gemini API สามารถเปลี่ยนอินพุตข้อความเป็นเสียงที่มีผู้พูดคนเดียวหรือหลายคน โดยใช้ความสามารถในการสร้างข้อความเป็นเสียง (TTS) ของ Gemini การสร้างข้อความเป็นเสียงควบคุมได้ ซึ่งหมายความว่าคุณสามารถรวมข้อมูลเมตาของเทิร์นที่มีโครงสร้าง (speech_metadata) และแท็กเสียงร้องในบรรทัดเพื่อกำหนดสไตล์ สำเนียง จังหวะ และโทนเสียงของเสียง

ความสามารถของ TTS แตกต่างจากการสร้างคำพูดที่ให้บริการผ่าน Live API ซึ่งออกแบบมาสำหรับเสียงแบบโต้ตอบที่ไม่มีโครงสร้าง รวมถึงอินพุตและเอาต์พุตแบบหลายรูปแบบ แม้ว่า Live API จะทำงานได้ดี ในบริบทการสนทนาแบบไดนามิก แต่ TTS ผ่าน Gemini API ได้รับการปรับแต่งสำหรับสถานการณ์ที่ต้องมีการอ่านข้อความที่แน่นอนพร้อมการควบคุม สไตล์และเสียงอย่างละเอียด เช่น การสร้างพอดแคสต์หรือหนังสือเสียง

คู่มือนี้จะแสดงวิธีสร้างเสียงแบบลำโพงเดียวและหลายลำโพงจากข้อความโดยใช้ Gemini 3.8 Flash TTS (gemini-3.8-flash-tts) และ Gemini 3.8 Flash-Lite TTS (gemini-3.8-flash-lite-tts)

ก่อนเริ่มต้น

ตรวจสอบว่าคุณใช้โมเดล TTS ของ Gemini ที่แสดงอยู่ในส่วนโมเดลที่รองรับ โปรดดูหัวข้อ เมื่อใดควรใช้โมเดลใด เพื่อเลือกโมเดลที่ดีที่สุดสำหรับปริมาณงานของคุณ

คุณอาจพบว่าการทดสอบโมเดล TTS ของ Gemini ใน AI Studio มีประโยชน์ก่อนที่จะเริ่มสร้าง

TTS แบบผู้พูดคนเดียว

หากต้องการแปลงข้อความเป็นเสียงแบบลำโพงเดียวด้วยโมเดล TTS ของ Gemini 3.8 ให้ส่ง ข้อความถอดเสียงตามคำพูดใน input แนบการจัดรูปแบบระดับการพูดโดยใช้คำอธิบายประกอบ speech_metadata และกำหนดค่าเสียงใน generation_config.speech_config คุณสามารถเลือกเสียงจากตัวเลือกเสียงที่สร้างไว้ล่วงหน้า คลังเสียงเพิ่มเติม (GET /v1beta/voices) รหัสการออกแบบเสียงที่กำหนดเอง (voice_...) หรือรหัสการจำลองเสียง (voice_... หรือvoicekey_...แบบไม่เก็บสถานะที่ไม่บังคับ)

ตัวอย่างนี้จะบันทึกเสียงเอาต์พุต WAV เริ่มต้น (audio/wav) จากโมเดลลงในไฟล์โดยตรง

Python

import base64
from google import genai

client = genai.Client()

interaction = client.interactions.create(
    model="gemini-3.8-flash-tts",
    input=[{
        "type": "user_input",
        "content": [{
            "type": "text",
            "text": "Have a wonderful day!",
            "annotations": [{
                "type": "speech_metadata",
                "style": "cheerful and friendly",
            }],
        }],
    }],
    response_format={"type": "audio"},
    generation_config={
        "speech_config": [
            {"voice": "Kore"},
        ]
    },
)

with open("out.wav", "wb") as f:
    f.write(base64.b64decode(interaction.output_audio.data))

JavaScript

import * as fs from 'node:fs';
import {GoogleGenAI} from '@google/genai';

async function main() {
   const client = new GoogleGenAI({});

   const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash-tts',
      input: [{
         type: 'user_input',
         content: [{
            type: 'text',
            text: 'Have a wonderful day!',
            annotations: [{
               type: 'speech_metadata',
               style: 'cheerful and friendly',
            }],
         }],
      }],
      response_format: { type: 'audio' },
      generation_config: {
         speech_config: [
            { voice: 'Kore' },
         ],
      },
   });

   const audioBuffer = Buffer.from(interaction.output_audio.data, 'base64');
   fs.writeFileSync('out.wav', audioBuffer);
}
await main();

Go

package main

import (
    "context"
    "encoding/base64"
    "encoding/binary"
    "log"
    "os"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

func saveWaveFile(filename string, pcmData []byte) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()

    sampleRate := uint32(24000)
    numChannels := uint16(1)
    bitsPerSample := uint16(16)
    byteRate := sampleRate * uint32(numChannels) * uint32(bitsPerSample/8)
    blockAlign := numChannels * (bitsPerSample / 8)
    dataSize := uint32(len(pcmData))

    f.WriteString("RIFF")
    binary.Write(f, binary.LittleEndian, uint32(36+dataSize))
    f.WriteString("WAVEfmt ")
    binary.Write(f, binary.LittleEndian, uint32(16))
    binary.Write(f, binary.LittleEndian, uint16(1))
    binary.Write(f, binary.LittleEndian, numChannels)
    binary.Write(f, binary.LittleEndian, sampleRate)
    binary.Write(f, binary.LittleEndian, byteRate)
    binary.Write(f, binary.LittleEndian, blockAlign)
    binary.Write(f, binary.LittleEndian, bitsPerSample)
    f.WriteString("data")
    binary.Write(f, binary.LittleEndian, dataSize)
    _, err = f.Write(pcmData)
    return err
}

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    generationConfig := &interactions.GenerationConfig{
        SpeechConfig: genai.Ptr(interactions.NewSpeechConfigUnion([]interactions.SpeechConfig{
            {Voice: genai.Ptr("Kore")},
        })),
    }

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.1-flash-tts-preview"),
            Input: interactions.NewInteractionsInput("Say cheerfully: Have a wonderful day!"),
            ResponseFormat: genai.Ptr(interactions.NewCreateModelInteractionResponseFormat(
                interactions.NewResponseFormat(interactions.AudioResponseFormat{}),
            )),
            GenerationConfig: generationConfig,
        }),
    })
    if err != nil {
        log.Fatal(err)
    }

    if res.Interaction.OutputAudio != nil && res.Interaction.OutputAudio.Data != nil {
        pcmBytes, err := base64.StdEncoding.DecodeString(*res.Interaction.OutputAudio.Data)
        if err != nil {
            log.Fatal(err)
        }
        if err := saveWaveFile("out.wav", pcmBytes); err != nil {
            log.Fatal(err)
        }
    }
}

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.8-flash-tts",
    "input": [{
      "type": "user_input",
      "content": [{
        "type": "text",
        "text": "Have a wonderful day!",
        "annotations": [{
          "type": "speech_metadata",
          "style": "cheerful and friendly"
        }]
      }]
    }],
    "response_format": {
      "type": "audio"
    },
    "generation_config": {
      "speech_config": [
        { "voice": "Kore" }
      ]
    }
  }'

คุณสามารถดึงข้อมูลเสียงที่สร้างขึ้นได้โดยใช้พร็อพเพอร์ตี้ interaction.output_audio ซึ่งจะแสดงผลบล็อกเสียงที่สร้างล่าสุด ดูรายละเอียดเกี่ยวกับ พร็อพเพอร์ตี้ความสะดวกได้ที่ ภาพรวมของการโต้ตอบ

TTS แบบหลายผู้พูด

สำหรับบทสนทนาที่มีผู้พูดหลายคน ให้กำหนดค่าผู้พูด 2 คนใน speech_config.speakers และส่งแต่ละเทิร์นเป็นรายการข้อความแยกกันพร้อมคำอธิบายประกอบ speech_metadata ที่ระบุ speaker และ style ระดับเทิร์น (ไม่บังคับ) ใช้ "mode": "conversational" เพื่อให้การสนทนาเป็นไปอย่างเป็นธรรมชาติ

Python

import base64
from google import genai

client = genai.Client()

interaction = client.interactions.create(
    model="gemini-3.8-flash-tts",
    input=[{
        "type": "user_input",
        "content": [
            {
                "type": "text",
                "text": "How's it going today Jane?",
                "annotations": [{
                    "type": "speech_metadata",
                    "speaker": "Joe",
                    "style": "cheerful and friendly",
                }],
            },
            {
                "type": "text",
                "text": "Not too bad, how about you? Ready to test these new voices?",
                "annotations": [{
                    "type": "speech_metadata",
                    "speaker": "Jane",
                    "style": "calm and relaxed",
                }],
            },
        ],
    }],
    response_format={"type": "audio"},
    generation_config={
        "speech_config": {
            "mode": "conversational",
            "speakers": [
                {"speaker": "Joe", "voice": "Puck"},
                {"speaker": "Jane", "voice": "Kore"},
            ],
        }
    },
)

with open("out.wav", "wb") as f:
    f.write(base64.b64decode(interaction.output_audio.data))

JavaScript

import * as fs from 'node:fs';
import {GoogleGenAI} from '@google/genai';

async function main() {
   const client = new GoogleGenAI({});

   const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash-tts',
      input: [{
         type: 'user_input',
         content: [
            {
               type: 'text',
               text: "How's it going today Jane?",
               annotations: [{
                  type: 'speech_metadata',
                  speaker: 'Joe',
                  style: 'cheerful and friendly',
               }],
            },
            {
               type: 'text',
               text: 'Not too bad, how about you? Ready to test these new voices?',
               annotations: [{
                  type: 'speech_metadata',
                  speaker: 'Jane',
                  style: 'calm and relaxed',
               }],
            },
         ],
      }],
      response_format: { type: 'audio' },
      generation_config: {
         speech_config: {
            mode: 'conversational',
            speakers: [
               { speaker: 'Joe', voice: 'Puck' },
               { speaker: 'Jane', voice: 'Kore' },
            ],
         },
      },
   });

   const audioBuffer = Buffer.from(interaction.output_audio.data, 'base64');
   fs.writeFileSync('out.wav', audioBuffer);
}

await main();

Go

package main

import (
    "context"
    "encoding/base64"
    "encoding/binary"
    "log"
    "os"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

func saveWaveFile(filename string, pcmData []byte) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()

    sampleRate := uint32(24000)
    numChannels := uint16(1)
    bitsPerSample := uint16(16)
    byteRate := sampleRate * uint32(numChannels) * uint32(bitsPerSample/8)
    blockAlign := numChannels * (bitsPerSample / 8)
    dataSize := uint32(len(pcmData))

    f.WriteString("RIFF")
    binary.Write(f, binary.LittleEndian, uint32(36+dataSize))
    f.WriteString("WAVEfmt ")
    binary.Write(f, binary.LittleEndian, uint32(16))
    binary.Write(f, binary.LittleEndian, uint16(1))
    binary.Write(f, binary.LittleEndian, numChannels)
    binary.Write(f, binary.LittleEndian, sampleRate)
    binary.Write(f, binary.LittleEndian, byteRate)
    binary.Write(f, binary.LittleEndian, blockAlign)
    binary.Write(f, binary.LittleEndian, bitsPerSample)
    f.WriteString("data")
    binary.Write(f, binary.LittleEndian, dataSize)
    _, err = f.Write(pcmData)
    return err
}

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    prompt := "TTS the following conversation between Joe and Jane:\n" +
        "Joe: How's it going today Jane?\n" +
        "Jane: Not too bad, how about you?"

    generationConfig := &interactions.GenerationConfig{
        SpeechConfig: genai.Ptr(interactions.NewSpeechConfigUnion([]interactions.SpeechConfig{
            {Speaker: genai.Ptr("Joe"), Voice: genai.Ptr("Kore")},
            {Speaker: genai.Ptr("Jane"), Voice: genai.Ptr("Puck")},
        })),
    }

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.1-flash-tts-preview"),
            Input: interactions.NewInteractionsInput(prompt),
            ResponseFormat: genai.Ptr(interactions.NewCreateModelInteractionResponseFormat(
                interactions.NewResponseFormat(interactions.AudioResponseFormat{}),
            )),
            GenerationConfig: generationConfig,
        }),
    })
    if err != nil {
        log.Fatal(err)
    }

    if res.Interaction.OutputAudio != nil && res.Interaction.OutputAudio.Data != nil {
        pcmBytes, err := base64.StdEncoding.DecodeString(*res.Interaction.OutputAudio.Data)
        if err != nil {
            log.Fatal(err)
        }
        if err := saveWaveFile("out.wav", pcmBytes); err != nil {
            log.Fatal(err)
        }
    }
}

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.8-flash-tts",
    "input": [{
      "type": "user_input",
      "content": [
        {
          "type": "text",
          "text": "How'\''s it going today Jane?",
          "annotations": [{
            "type": "speech_metadata",
            "speaker": "Joe",
            "style": "cheerful and friendly"
          }]
        },
        {
          "type": "text",
          "text": "Not too bad, how about you? Ready to test these new voices?",
          "annotations": [{
            "type": "speech_metadata",
            "speaker": "Jane",
            "style": "calm and relaxed"
          }]
        }
      ]
    }],
    "response_format": {
      "type": "audio"
    },
    "generation_config": {
      "speech_config": {
        "mode": "conversational",
        "speakers": [
          { "speaker": "Joe", "voice": "Puck" },
          { "speaker": "Jane", "voice": "Kore" }
        ]
      }
    }
  }'

ควบคุมรูปแบบคำพูดด้วยข้อมูลเมตาและแท็ก

TTS ของ Gemini 3.8 จะถือว่าฟิลด์ text เป็นสำเนาที่ตรงตามต้นฉบับอย่างเคร่งครัด หากต้องการ ควบคุมการนำเสนอโดยไม่ต้องอ่านคำแนะนำบนเวที ให้แบ่งคำสั่งตามขอบเขตดังนี้

  • การพูดในระดับคำพูดอย่างต่อเนื่อง (speech_metadata.style): ใส่อารมณ์ สไตล์การพูด ความขึ้นลงของเสียง จังหวะ และระดับเสียงที่ใช้ตลอดทั้งคำพูด ในฟิลด์ style (เช่น "style": "whispered urgently" "style": "out of breath" หรือ "style": "warm and enthusiastic")
  • เหตุการณ์ ณ จุดใดจุดหนึ่ง (แท็กในบรรทัด): วางเสียงพูดที่ไม่ได้เป็นคำพูดชั่วคราว หรือหยุดชั่วคราวไว้ในข้อความถอดเสียงโดยตรงโดยใช้เครื่องหมายวงเล็บมุม (เช่น "Wait... <short pause> did you hear that? <sigh>" หรือ "Excuse me <cough> as I was saying...")

ดูแนวทางปฏิบัติแนะนำที่ครอบคลุมได้ในคู่มือการใช้พรอมต์

Go

package main

import (
    "context"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    transcriptRes, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput(
                "Generate a short transcript around 100 words that reads " +
                    "like it was clipped from a podcast by excited herpetologists. " +
                    "The hosts names are Dr. Anya and Liam.",
            ),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }

    var transcript string
    if transcriptRes.Interaction.OutputText != nil {
        transcript = *transcriptRes.Interaction.OutputText
    }

    generationConfig := &interactions.GenerationConfig{
        SpeechConfig: genai.Ptr(interactions.NewSpeechConfigUnion([]interactions.SpeechConfig{
            {Speaker: genai.Ptr("Dr. Anya"), Voice: genai.Ptr("Kore")},
            {Speaker: genai.Ptr("Liam"), Voice: genai.Ptr("Puck")},
        })),
    }

    ttsRes, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.1-flash-tts-preview"),
            Input: interactions.NewInteractionsInput(transcript),
            ResponseFormat: genai.Ptr(interactions.NewCreateModelInteractionResponseFormat(
                interactions.NewResponseFormat(interactions.AudioResponseFormat{}),
            )),
            GenerationConfig: generationConfig,
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    _ = ttsRes
}

การสร้างคำพูดแบบสตรีมมิง

คุณสามารถสตรีมเสียงที่สร้างขึ้นขณะที่กำลังสังเคราะห์ได้โดยการตั้งค่า stream: true ไม่เหมือนกับคำขอแบบเอกภาค (ซึ่งจะแสดงผลไฟล์ WAV ที่สมบูรณ์พร้อมส่วนหัว RIFF) คำขอสตรีมมิงจะแสดงผลก้อนข้อมูล PCM เชิงเส้นแบบ 16 บิตที่ลงนามแบบ Little-Endian ดิบที่ไม่มีส่วนหัว (audio/l16, 24 kHz, โมโน) โดยค่าเริ่มต้น เพื่อให้สามารถเล่นหรือต่อก้อนข้อมูลเสียงได้อย่างต่อเนื่องโดยไม่มีส่วนหัวของคอนเทนเนอร์

Python

import base64
from google import genai

client = genai.Client()

stream = client.interactions.create(
    model="gemini-3.8-flash-tts",
    input=[{
        "type": "user_input",
        "content": [{
            "type": "text",
            "text": "Have a wonderful day!",
            "annotations": [{
                "type": "speech_metadata",
                "style": "cheerful and friendly",
            }],
        }],
    }],
    response_format={"type": "audio"},
    generation_config={
        "speech_config": [
            {"voice": "Kore"},
        ]
    },
    stream=True,
)

for event in stream:
    if event.event_type == "step.delta":
        if event.delta.type == "audio":
            audio_data = base64.b64decode(event.delta.data)
            # Process the audio chunk (e.g. play it or write to a file)

JavaScript

import {GoogleGenAI} from '@google/genai';

async function main() {
   const client = new GoogleGenAI({});

   const stream = await client.interactions.create({
      model: 'gemini-3.8-flash-tts',
      input: [{
         type: 'user_input',
         content: [{
            type: 'text',
            text: 'Have a wonderful day!',
            annotations: [{
               type: 'speech_metadata',
               style: 'cheerful and friendly',
            }],
         }],
      }],
      response_format: { type: 'audio' },
      generation_config: {
         speech_config: [
            { voice: 'Kore' },
         ],
      },
      stream: true,
   });

   for await (const event of stream) {
      if (event.event_type === 'step.delta') {
         if (event.delta.type === 'audio') {
            const audioBuffer = Buffer.from(event.delta.data, 'base64');
            // Process the audio buffer
         }
      }
   }
}
await main();

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  --no-buffer \
  -d '{
    "model": "gemini-3.8-flash-tts",
    "input": [{
      "type": "user_input",
      "content": [{
        "type": "text",
        "text": "Have a wonderful day!",
        "annotations": [{
          "type": "speech_metadata",
          "style": "cheerful and friendly"
        }]
      }]
    }],
    "response_format": {
      "type": "audio"
    },
    "generation_config": {
      "speech_config": [
        { "voice": "Kore" }
      ]
    },
    "stream": true
  }'

รูปแบบเอาต์พุตเสียง

โมเดล TTS ของ Gemini 3.8 ใช้รูปแบบเสียงเริ่มต้นที่แตกต่างกัน ขึ้นอยู่กับว่าคำขอเป็นแบบเอกภาคหรือแบบสตรีมมิง ดังนี้

  • คำขอแบบเอกภาค (stream=False): แสดงเสียง WAV (audio/wav) ที่สมบูรณ์พร้อมส่วนหัว RIFF มาตรฐาน (24 kHz, โมโน, 16 บิตแบบมีเครื่องหมาย Little-Endian PCM) คุณสามารถบันทึกไบต์เสียงที่ถอดรหัสแล้วลงในไฟล์ .wav ได้โดยตรงโดยไม่ต้อง เพิ่มส่วนหัว WAV ด้วยตนเอง
  • คำขอสตรีม (stream=True): ส่งคืนก้อนข้อมูล Linear PCM แบบดิบที่ไม่มีส่วนหัว (audio/l16) (24 kHz, โมโน, 16 บิต, PCM แบบ Little-Endian ที่มีเครื่องหมาย) โดย ค่าเริ่มต้นเพื่อให้สตรีมหรือต่อก้อนข้อมูลได้อย่างต่อเนื่องโดยไม่ต้องมี ส่วนหัวของคอนเทนเนอร์ในแต่ละก้อนข้อมูล

หากต้องการขอการเข้ารหัสเสียงหรืออัตราตัวอย่างอื่น ให้กำหนดค่า mime_type และ sample_rate ที่ไม่บังคับภายใน response_format ดังนี้

รูปแบบ ค่า mime_type คำอธิบาย
WAV (ค่าเริ่มต้นแบบเอกภาค) "audio/wav" ไฟล์ WAV ที่ไม่ได้บีบอัดซึ่งมีส่วนหัว RIFF (PCM แบบ little-endian ที่มีเครื่องหมาย 16 บิต, โมโน, 24 kHz โดยค่าเริ่มต้น) ค่าเริ่มต้นสำหรับคำขอแบบเอกภาค
PCM ดิบ (L16) (ค่าเริ่มต้นสำหรับการสตรีม) "audio/l16" เสียง PCM เชิงเส้นแบบ Little-Endian ที่ลงนาม 16 บิตแบบไม่มีส่วนหัวและไม่ได้บีบอัด (24 kHz, โมโน) ค่าเริ่มต้นสำหรับคำขอสตรีม
Mu-law "audio/mulaw" เสียงที่เข้ารหัส G.711 mu-law แบบ 8 บิต (ใช้กันโดยทั่วไปในระบบโทรศัพท์/IVR ของอเมริกาเหนือและญี่ปุ่น)
A-law "audio/alaw" เสียงที่เข้ารหัส G.711 A-law แบบ 8 บิต (ใช้กันทั่วไปในระบบโทรศัพท์ของยุโรปและต่างประเทศ)

คุณยังระบุ sample_rate เป็นเฮิรตซ์ได้ด้วย (เช่น 24000, 16000 หรือ 8000)

Python

import base64
from google import genai

client = genai.Client()

interaction = client.interactions.create(
    model="gemini-3.8-flash-tts",
    input=[{
        "type": "user_input",
        "content": [{
            "type": "text",
            "text": "Have a wonderful day!",
            "annotations": [{
                "type": "speech_metadata",
                "style": "cheerful and friendly",
            }],
        }],
    }],
    response_format={
        "type": "audio",
        "mime_type": "audio/l16",  # "audio/wav" (default), "audio/l16", "audio/mulaw", or "audio/alaw"
        "sample_rate": 24000,
    },
    generation_config={
        "speech_config": [
            {"voice": "Kore"},
        ]
    },
)

with open("out.pcm", "wb") as f:
    f.write(base64.b64decode(interaction.output_audio.data))

JavaScript

import * as fs from 'node:fs';
import {GoogleGenAI} from '@google/genai';

async function main() {
   const client = new GoogleGenAI({});

   const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash-tts',
      input: [{
         type: 'user_input',
         content: [{
            type: 'text',
            text: 'Have a wonderful day!',
            annotations: [{
               type: 'speech_metadata',
               style: 'cheerful and friendly',
            }],
         }],
      }],
      response_format: {
         type: 'audio',
         mime_type: 'audio/l16', // 'audio/wav' (default), 'audio/l16', 'audio/mulaw', or 'audio/alaw'
         sample_rate: 24000,
      },
      generation_config: {
         speech_config: [
            { voice: 'Kore' },
         ],
      },
   });

   const audioBuffer = Buffer.from(interaction.output_audio.data, 'base64');
   fs.writeFileSync('out.pcm', audioBuffer);
}
await main();

Go

package main

import (
    "context"
    "encoding/base64"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    generationConfig := &interactions.GenerationConfig{
        SpeechConfig: genai.Ptr(interactions.NewSpeechConfigUnion([]interactions.SpeechConfig{
            {Voice: genai.Ptr("Kore")},
        })),
    }

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.1-flash-tts-preview"),
            Input: interactions.NewInteractionsInput("Say cheerfully: Have a wonderful day!"),
            ResponseFormat: genai.Ptr(interactions.NewCreateModelInteractionResponseFormat(
                interactions.NewResponseFormat(interactions.AudioResponseFormat{}),
            )),
            GenerationConfig: generationConfig,
            Stream:           genai.Ptr(true),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }

    stream := res.InteractionSSEStreamEvent
    defer stream.Close()

    for stream.Next() {
        event := stream.Value()
        if stepDelta := event.GetDataStepDelta(); stepDelta != nil {
            if audioDelta := stepDelta.GetDeltaAudio(); audioDelta != nil && audioDelta.Data != nil {
                audioData, err := base64.StdEncoding.DecodeString(*audioDelta.Data)
                if err != nil {
                    log.Fatal(err)
                }
                // Process the audio chunk (e.g. play it or write to a file)
                _ = audioData
            }
        }
    }
    if err := stream.Err(); err != nil {
        log.Fatal(err)
    }
}

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.8-flash-tts",
    "input": [{
      "type": "user_input",
      "content": [{
        "type": "text",
        "text": "Have a wonderful day!",
        "annotations": [{
          "type": "speech_metadata",
          "style": "cheerful and friendly"
        }]
      }]
    }],
    "response_format": {
      "type": "audio",
      "mime_type": "audio/l16",
      "sample_rate": 24000
    },
    "generation_config": {
      "speech_config": [
        { "voice": "Kore" }
      ]
    }
  }'

ตัวเลือกเสียง

TTS ของ Gemini 3.8 รองรับ 4 วิธีในการเลือกหรือสร้างเสียง ดังนี้

  1. เสียงสตูดิโอที่สร้างไว้ล่วงหน้า: เสียงที่คัดสรรมา 30 เสียงซึ่งแสดงอยู่ในตารางต่อไปนี้
  2. คลังเสียงเพิ่มเติม: เสียงเพิ่มเติมอีกหลายร้อยเสียงในภาษา สำเนียง และต้นแบบตัวละครต่างๆ ซึ่งเข้าถึงได้โดยใช้ client.voices.list() (GET /v1beta/voices)
  3. การออกแบบเสียง: สร้างลักษณะเสียงร้องที่กำหนดเองจากคำอธิบายภาษาธรรมชาติใน Google AI Studio หรือใช้ POST /v1beta/voices (type="prompted" ซึ่งจะแสดงรหัส voice_... ที่คงอยู่และตัวอย่าง sample_audio WAV ใน CreateVoice และ GetVoice)
  4. การจำลองเสียง: จำลองเสียงของ ผู้พูดจากเสียงอ้างอิงและเสียงที่ได้รับความยินยอมใน Google AI Studio หรือใช้ POST /v1beta/voices (type="replicated", store=True แบบถาวรโดย ค่าเริ่มต้นหรือ store=False แบบไม่เก็บสถานะที่ไม่บังคับ)

ขีดจำกัดและ TTL ของเสียงที่กำหนดเอง

พิมพ์ด้วยเสียง โหมดพื้นที่เก็บข้อมูล โควต้า / ขีดจำกัด การเก็บรักษา (TTL)
เสียงที่มีสถานะ (voice_..., พรอมต์หรือจำลอง) store=True เสียง 200 เสียงต่อโปรเจ็กต์ (ใช้ร่วมกันในเสียงที่พรอมต์และจำลอง) 1 ปี
คีย์เสียงแบบไม่เก็บสถานะ (voicekey_..., ทำซ้ำ) store=False จัดการโดยไคลเอ็นต์ 7 วัน

เสียงที่สร้างไว้ล่วงหน้า

Zephyr -- Bright Puck - Upbeat Charon - ให้ข้อมูล
เกาหลี -- Firm Fenrir - ตื่นเต้นง่าย Leda -- อ่อนเยาว์
Orus -- Firm Aoede -- Breezy Callirrhoe -- สบายๆ
Autonoe -- Bright Enceladus -- Breathy Iapetus -- Clear
Umbriel -- สบายๆ Algieba -- Smooth Despina -- Smooth
Erinome -- ล้าง Algenib -- Gravelly Rasalgethi -- ให้ข้อมูล
Laomedeia -- Upbeat Achernar -- Soft Alnilam -- Firm
Schedar -- Even Gacrux -- ผู้ใหญ่ Pulcherrima -- Forward
Achird -- เป็นมิตร Zubenelgenubi -- สบายๆ Vindemiatrix -- อ่อนโยน
Sadachbia -- มีชีวิตชีวา Sadaltager -- มีความรู้ Sulafat -- Warm

คลังเสียงและตัวกรองที่ขยาย

นอกเหนือจากเสียงสตูดิโอแนะนำ 30 เสียงในตารางก่อนหน้าแล้ว คลังเสียงเพิ่มเติมยังมีเสียงอีกหลายร้อยเสียงในภาษาต่างๆ สำเนียงในภูมิภาค บุคลิกตัวละคร และโดเมนต่างๆ คุณสามารถเรียกดู กรอง และ ทดลองใช้คลังเสียงทั้งหมดแบบอินเทอร์แอกทีฟใน Google AI Studio หรือค้นหาแบบเป็นโปรแกรมโดยใช้ client.voices.list() (GET /v1beta/voices โดยใช้ google-genai 2.25.0 ขึ้นไป / @google/genai 2.24.0 ขึ้นไป)

ListVoices จะแสดงเสียงที่คุณบันทึกไว้ (เรียงจากใหม่สุดก่อน) ตามด้วย เสียงในแคตตาล็อกที่สร้างไว้ล่วงหน้าซึ่งตรงกับเกณฑ์ตัวกรองของคุณ เมื่อส่งค่าหลายค่า สำหรับตัวกรองรายการ ระบบจะแสดงเสียงที่ตรงกับค่าใดก็ได้ในตัวกรองนั้น (OR) ในขณะที่พารามิเตอร์ตัวกรองที่แตกต่างกันจะรวมกับ AND

พารามิเตอร์ ประเภท คำอธิบาย
language_code list[str] แท็กภาษา BCP-47 (เช่น ["en-US", "en-GB"]) การจับคู่ที่ตรงกันทั้งหมดโดยไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
region_code list[str] รหัสภูมิภาค ISO 3166-1 alpha-2 หรือ UN M.49 (เช่น ["US", "GB"])
accent list[str] คำอธิบายสำเนียงในภูมิภาค (เช่น ["American", "British"])
gender list[str] การแสดงออกทางเพศที่รับรู้ ("female", "male" หรือ "neutral")
pitch list[str] การจัดประเภทระดับเสียงร้อง ("low", "medium" หรือ "high")
persona list[str] บุคลิกหรือต้นแบบตัวละครที่มีเสียงร้อง (เช่น ["Warm, Friendly"], ["Narrator"])
contexts (context ใน REST) list[str] โดเมนการใช้งานที่เหมาะสม (เช่น ["Audiobook", "Conversational", "News"])
type (type_ ใน Python) list[str] กรองตามแหล่งที่มาของเสียง: "prebuilt", "prompted" (การออกแบบเสียง) หรือ "replicated" (การจำลองเสียง)
search str การค้นหาสตริงย่อยแบบข้อความอิสระจะจับคู่โดยไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่กับทั้ง display_name และ description
page_size int จำนวนเสียงสูงสุดที่แสดงต่อหน้า (ค่าเริ่มต้นคือ 50 สูงสุดคือ 1000)
page_token str โทเค็นจาก response.next_page_token เพื่อดึงข้อมูลผลลัพธ์หน้าถัดไป

Python

from google import genai

client = genai.Client()

# Filter the Voice Library by language, gender, pitch, domain context, and keyword
response = client.voices.list(
    language_code=["en-US", "en-GB"],
    gender=["female"],
    pitch=["medium", "low"],
    contexts=["Audiobook", "Conversational"],
    type_=["prebuilt"],
    search="warm",
    page_size=50,
)

for voice in response.voices or []:
    print(
        f"{voice.id} | {voice.display_name} ({voice.language_code},"
        f" {voice.accent}, {voice.gender}, pitch={voice.pitch}):"
        f" {voice.description}"
    )

JavaScript

import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI();

// Filter the Voice Library by language, gender, pitch, domain context, and keyword
const response = await ai.voices.list({
  language_code: ["en-US", "en-GB"],
  gender: ["female"],
  pitch: ["medium", "low"],
  contexts: ["Audiobook", "Conversational"],
  type: ["prebuilt"],
  search: "warm",
  page_size: 50,
});

for (const voice of response.voices ?? []) {
  console.log(
    `${voice.id} | ${voice.display_name} (${voice.language_code}, ${voice.accent}, ${voice.gender}, pitch=${voice.pitch}): ${voice.description}`
  );
}

REST

curl -G "https://generativelanguage.googleapis.com/v1beta/voices" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  --data-urlencode "language_code=en-US" \
  --data-urlencode "language_code=en-GB" \
  --data-urlencode "gender=female" \
  --data-urlencode "pitch=medium" \
  --data-urlencode "context=Audiobook" \
  --data-urlencode "type=prebuilt" \
  --data-urlencode "search=warm" \
  --data-urlencode "page_size=50"

ภาษาที่รองรับ

โมเดล TTS จะตรวจหาภาษาที่ป้อนโดยอัตโนมัติ TTS ของ Gemini 3.8 Flash (gemini-3.8-flash-tts) รองรับ 130 ภาษา และ TTS ของ Gemini 3.8 Flash-Lite (gemini-3.8-flash-lite-tts) รองรับ 101 ภาษา

ภาษา TTS ของ Gemini 3.8 Flash TTS ของ Gemini 3.8 Flash-Lite
อาเจะฮ์ (อักษรอาหรับ) ✔️ ✔️
อาฟรีกานส์ ✔️ ✔️
อะคัน ✔️ ✔️
อัมฮาริก ✔️ ✔️
อาร์เมเนีย ✔️ ✔️
อัสสัม ✔️ ✔️
อวธี ✔️ ✔️
บาหลี ✔️ ✔️
เบงกอล ✔️ ✔️
บันจาร์ (อักษรอาหรับ) ✔️ —
บันจาร์ (อักษรละติน) ✔️ ✔️
แบชเคียร์ ✔️ —
บาสก์ ✔️ ✔️
เบลารุส ✔️ ✔️
เบมบา ✔️ —
โภชปุระ ✔️ ✔️
บอสเนีย ✔️ ✔️
บูกิส ✔️ ✔️
บัลแกเรีย ✔️ ✔️
พม่า ✔️ —
จีนกวางตุ้ง ✔️ ✔️
คาตาลัน ✔️ ✔️
ซีบัวโน ✔️ ✔️
เคิร์ดตอนกลาง ✔️ ✔️
ฉัตติสครห์ ✔️ ✔️
จีน (อักษรฮั่น) ✔️ ✔️
จีน (อักษรฮั่น) ✔️ ✔️
ตาตาร์ไครเมีย ✔️ —
โครเอเชีย ✔️ ✔️
เช็ก ✔️ ✔️
เดนมาร์ก ✔️ ✔️
ดัตช์ ✔️ ✔️
ดิวลา ✔️ —
ซองคา ✔️ —
อาหรับอียิปต์ ✔️ ✔️
อังกฤษ ✔️ ✔️
เอสโตเนีย ✔️ ✔️
ฟิลิปปินส์ ✔️ ✔️
ฟินแลนด์ ✔️ —
ฝรั่งเศส ✔️ ✔️
กาลิเชียน ✔️ ✔️
กันดา ✔️ ✔️
จอร์เจีย ✔️ ✔️
เยอรมัน ✔️ ✔️
กรีก ✔️ ✔️
กวารานี ✔️ —
คุชราต ✔️ ✔️
เฮติครีโอล ✔️ ✔️
มองโกเลียคาลคา ✔️ ✔️
เฮาซา ✔️ ✔️
ฮีบรู ✔️ ✔️
ฮินดี ✔️ ✔️
ฮังการี ✔️ ✔️
ไอซ์แลนด์ ✔️ ✔️
อิกโบ ✔️ —
อีโลโก ✔️ ✔️
อินโดนีเซีย ✔️ ✔️
เปอร์เซียอิหร่าน ✔️ ✔️
อิตาลี ✔️ ✔️
ญี่ปุ่น ✔️ ✔️
ชวา ✔️ ✔️
คาไบล์ ✔️ —
คัมบา ✔️ ✔️
กันนาดา ✔️ ✔️
แคชเมียร์ (อักษรอาหรับ) ✔️ ✔️
แคชเมียร์ (สคริปต์เทวนาครี) ✔️ ✔️
คาซัค ✔️ ✔️
เขมร ✔️ ✔️
คิคูยู ✔️ ✔️
คินยารวันดา ✔️ ✔️
คองโก ✔️ ✔️
เกาหลี ✔️ ✔️
คีร์กิซ ✔️ ✔️
ภาษาลาว ✔️ ✔️
ลัตกาเรียน ✔️ —
ลิงกาลา ✔️ ✔️
ลิทัวเนีย ✔️ —
ลักเซมเบิร์ก ✔️ —
มาซีโดเนีย ✔️ ✔️
มคธี ✔️ ✔️
ไมถิลี ✔️ ✔️
มาลายาลัม ✔️ ✔️
มอลตา ✔️ ✔️
มานิพูรี ✔️ ✔️
มราฐี ✔️ ✔️
มินังกาเบา (อักษรอาหรับ) ✔️ ✔️
มินังกาเบา (สคริปต์ละติน) ✔️ —
มิโซ ✔️ ✔️
เนปาล (ภาษาเฉพาะ) ✔️ ✔️
ฟุลฟุลเดของไนจีเรีย ✔️ ✔️
อาเซอร์ไบจานเหนือ ✔️ ✔️
โซโทเหนือ ✔️ ✔️
อุซเบกเหนือ ✔️ ✔️
นอร์เวย์บุคมอล ✔️ ✔️
นีนอสก์ของนอร์เวย์ ✔️ ✔️
เนียนจา ✔️ ✔️
ออกซิแทน ✔️ —
โอเดีย (ภาษาเฉพาะ) ✔️ ✔️
ปังกาซินัน ✔️ —
เปอร์เซีย (อัฟกานิสถาน) ✔️ ✔️
โปแลนด์ ✔️ ✔️
โปรตุเกส ✔️ ✔️
ปัญจาบ ✔️ ✔️
โรมาเนีย ✔️ ✔️
รัสเซีย ✔️ ✔️
สันถาลี ✔️ ✔️
เซอร์เบีย ✔️ ✔️
สินธี ✔️ —
สิงหล ✔️ ✔️
สโลวัก ✔️ ✔️
สโลวีเนีย ✔️ —
โซมาลี ✔️ —
อาเซอร์ไบจานใต้ ✔️ ✔️
ปาทานใต้ ✔️ ✔️
โซโทใต้ ✔️ —
สเปน ✔️ ✔️
อาหรับมาตรฐาน (สคริปต์อาหรับ) ✔️ ✔️
อาหรับมาตรฐาน (สคริปต์ละติน) ✔️ ✔️
ลัตเวียมาตรฐาน ✔️ ✔️
ภาษามลายูมาตรฐาน ✔️ ✔️
สวาฮีลี (ภาษาเฉพาะ) ✔️ —
สวาติ ✔️ —
สวีเดน ✔️ —
ทาจิก ✔️ —
ทมิฬ ✔️ ✔️
เตลูกู ✔️ ✔️
ไทย ✔️ —
ทิกรินยา ✔️ —
แอลบาเนียนแบบทอสก์ ✔️ —
อุยกูร์ ✔️ —

รุ่นที่รองรับ

รุ่น ผู้พูดคนเดียว ผู้พูดหลายคน การออกแบบเสียง การจำลองเสียง
TTS ของ Gemini 3.8 Flash (gemini-3.8-flash-tts) ✔️ ✔️ ✔️ ✔️
TTS ของ Gemini 3.8 Flash-Lite (gemini-3.8-flash-lite-tts) ✔️ ✔️ ✔️ ✔️
ตัวอย่าง TTS ของ Gemini 3.1 Flash ✔️ ✔️ — —
TTS เวอร์ชันตัวอย่างของ Gemini 2.5 Pro ✔️ ✔️ — —

ควรใช้โมเดลใดเมื่อใด

โมเดล TTS ของ Gemini 3.8 ทั้ง 2 โมเดลใช้สคีมา API และรูปแบบพรอมต์เดียวกัน ซึ่งช่วยให้คุณสลับไปมาระหว่างโมเดลทั้ง 2 ได้ด้วยการเปลี่ยนพารามิเตอร์เพียงรายการเดียว

  • ใช้ Gemini 3.8 Flash TTS (gemini-3.8-flash-tts) เมื่อความเที่ยงตรงของเสียง การแสดงที่ซับซ้อน และการควบคุมการแสดงออกเป็นสิ่งสำคัญอันดับแรก เหมาะอย่างยิ่งสำหรับงานสร้างสรรค์ระดับสตูดิโอ บทสนทนาที่ซับซ้อนซึ่งมีลำโพงหลายตัว แท็กเสียงร้องที่หนักแน่น การออกเสียงที่ยาก สำเนียงประจำภูมิภาคหรือสำเนียงของชนกลุ่มน้อย และคำบรรยายแบบยาว ที่ต้องใช้เสียงที่มั่นคงและมีเสียงรบกวนในห้องที่เสถียร
  • ใช้ Gemini 3.8 Flash-Lite TTS (gemini-3.8-flash-lite-tts) เป็นเครื่องมือที่รวดเร็ว คุ้มค่า แทน gemini-3.1-flash-tts-preview โดยได้รับการเพิ่มประสิทธิภาพสำหรับ การผลิตจำนวนมาก การส่งต่อตัวแทนเสียงสนทนา ฟีเจอร์อ่านออกเสียง การจำลองเสียงที่เชื่อถือได้ และเสียงพูดแบบลำโพงเดี่ยวในชีวิตประจำวัน ในภาษาหลักๆ

คำแนะนำในการย้ายข้อมูล

หากคุณย้ายข้อมูลจากโมเดล TTS ของ Gemini gemini-3.1-flash-tts-preview หรือรุ่นก่อนหน้าไปเป็น TTS ของ Gemini 3.8 ให้ทำดังนี้

  1. ย้ายคำสั่งระดับเลี้ยวไปยัง speech_metadata: TTS ของ Gemini 3.8 จะถือว่า ข้อความที่ป้อนเป็นสำเนาที่ตรงตามต้นฉบับอย่างเคร่งครัด ย้ายวิธีการนำส่งที่ต่อเนื่อง (style เช่น "whispering", "out of breath" หรือ "speaking slowly") และป้ายกำกับผู้พูด (speaker) ไปยังคำอธิบายประกอบที่มีโครงสร้าง speech_metadata แทนที่จะฝังคำแนะนำบนเวทีในข้อความ ถอดเสียง
  2. ใช้แท็กในบรรทัดที่มีเครื่องหมายวงเล็บปีกกาสำหรับเหตุการณ์เสียงร้อง ณ จุดใดจุดหนึ่งเท่านั้น: เก็บเสียงร้องที่ไม่ใช่คำพูดและหยุดชั่วคราวในบรรทัดในข้อความถอดเสียงโดยใช้เครื่องหมายวงเล็บปีกกา (เช่น <laugh>, <sigh>, <cough>, <breath> หรือ <short pause>) หลีกเลี่ยงแท็กเอฟเฟกต์เสียง (เช่น เสียงปรบมือหรือเสียงทุ้ม) และใส่รูปแบบการนำเสนอใน speech_metadata.style
  3. ระบุ speaker ในทุกๆ เทิร์นในคำขอแบบหลายผู้พูด: ทุกๆ เทิร์นในคำขอแบบหลายผู้พูดต้องมี speaker อยู่ภายใน speech_metadata ที่ตรงกับผู้พูดที่กำหนดค่าไว้
  4. ออกแบบเพอร์โซนาล่วงหน้าด้วยการออกแบบเสียง: แทนที่บล็อกหลายย่อหน้า "Audio Profile" หรือ "Director's Notes" ด้วยเสียงที่กำหนดเองซึ่งสร้างขึ้น ในการออกแบบเสียง จากนั้นส่ง voice_... รหัสนั้นผ่านคำขอ TTS โดยมีสตริง style ที่ว่างเปล่าหรือมีข้อมูลน้อยที่สุด
  5. พิจารณาเอาต์พุต WAV (audio/wav) เริ่มต้นในคำขอแบบเอกภาคี: โมเดล TTS ของ Gemini 3.8 จะแสดงผลเสียง WAV (audio/wav) พร้อมส่วนหัว RIFF มาตรฐานโดยค่าเริ่มต้นสำหรับคำขอแบบเอกภาคี ซึ่งแตกต่างจากโมเดล TTS ของ gemini-3.1-flash-tts-preview และรุ่นก่อนหน้า (ซึ่งแสดงผล PCM ดิบแบบไม่มีส่วนหัว audio/l16 โดยค่าเริ่มต้น)
    • หากก่อนหน้านี้โค้ดของคุณห่อไบต์ PCM ดิบไว้ในส่วนหัวของ WAV (เช่น ใช้โมดูล wave หรือ ffmpeg ของ Python) ให้นำ Wrapper ส่วนหัวที่สร้างขึ้นเองออก แล้วเขียนไบต์ที่ส่งคืนไปยังไฟล์ .wav โดยตรง
    • หากไปป์ไลน์ของคุณต้องใช้เสียง PCM ดิบแบบไม่มีส่วนหัว, mu-law หรือ A-law ให้ตั้งค่า response_format เป็น "audio/l16", "audio/mulaw" หรือ "audio/alaw" อย่างชัดเจน ดูรูปแบบเอาต์พุตเสียง

คำแนะนำในการเขียนพรอมต์

โมเดล TTS ของ Gemini 3.8 จะถือว่าข้อความอินพุตเป็นข้อความถอดเสียงตามคำพูดอย่างเคร่งครัด ซึ่งต่างจากโมเดลเวอร์ชันตัวอย่างก่อนหน้านี้ที่ฝังคำสั่งบนเวทีไว้ในข้อความธรรมดา TTS ของ Gemini 3.8 จะแยกคำสั่งระดับการพูดที่ต่อเนื่อง (speech_metadata) ออกจากแท็กเสียงพูดในบรรทัด ณ จุดใดจุดหนึ่ง

ฟิลด์รูปแบบเทียบกับแท็กในบรรทัด

แยกคำสั่งประสิทธิภาพตามขอบเขต

  • การนำส่งระดับคำพูด (speech_metadata.style): ใส่แอตทริบิวต์การนำส่งที่ต่อเนื่อง เช่น อารมณ์ ความผันแปรของระดับเสียง จังหวะโดยรวม หรือสไตล์การนำส่ง (เช่น "whispering", "out of breath", "muttering" หรือ "sarcastic") ลงใน ฟิลด์ style ของ speech_metadata หากต้องการสร้างตัวละครและ การแสดงที่สม่ำเสมอตลอดการสนทนา ให้ออกแบบบุคลิกภาพล่วงหน้าในการออกแบบเสียง และใช้ style เฉพาะสำหรับการปรับแต่งระดับเทิร์นที่ไม่บังคับ
  • เหตุการณ์ ณ จุดใดจุดหนึ่ง (แท็กในบรรทัด): ใส่เสียงพูดที่ไม่ได้เป็นคำพูด เสียงหายใจ หรือการหยุดชั่วคราวในบรรทัดภายในข้อความถอดเสียงโดยใช้วงเล็บเหลี่ยม (<cough>, <breath>, <sigh>, <short pause>) ใช้วงเล็บเหลี่ยม (<...>) เพื่อให้ได้คุณภาพเสียงสูงสุด และใช้เสียงพูดของมนุษย์แทนเอฟเฟกต์เสียงที่ไม่มีเสียงพูด
ขอบเขต ตำแหน่งที่ควรวาง ตัวอย่าง
ระดับเทิร์น (คงที่ตลอดเทิร์น) speech_metadata.style "angry tone" "speaking rapidly" "out of breath" "whispers" "sarcastic"
ช่วงเวลา (เกิดขึ้นที่คำใดคำหนึ่ง) ใน text (<...>) "<cough> Thank you all for coming tonight! <throat-clearing> As I was saying..."

จังหวะการดำเนินเรื่องและการหยุดชั่วคราว

คุณควบคุมจังหวะและความเงียบได้ 3 ระดับความละเอียด ดังนี้

  • เครื่องหมายวรรคตอนและจุดไข่ปลา: ใช้คอมมา ขีดกลาง (--) และจุดไข่ปลา (...) เพื่อแสดงการลังเลในการสนทนาที่เป็นธรรมชาติ
  • แท็กหยุดชั่วคราวในบรรทัด: แทรก <short pause> หรือ <long pause> ที่จุดที่แน่นอน ในสคริปต์ที่ผู้พูดควรหยุดชั่วคราว text Hold on, let me think... <short pause> Alright, I've got it.
  • อัตราการพูดระดับคำ: ตั้งค่า "style": "speaking rapidly" หรือ "style": "speaking slowly" ใน speech_metadata เพื่อควบคุมอัตราการพูด ตลอดทั้งคำ

ทำนองและระดับเสียง

ใช้ speech_metadata.style เพื่อควบคุมการออกเสียง ระดับเสียง และการผันเสียงใน แต่ละช่วง (เช่น "style": "high pitch, cheerful and excited inflection" หรือ "style": "monotone and flat") หากอารมณ์หรือการออกเสียงเปลี่ยนไประหว่างบทสนทนา ให้ แยกสคริปต์ออกเป็นช่วงๆ โดยมีค่า style ที่แตกต่างกันสำหรับแต่ละช่วง

การเน้น

ใช้อักษรตัวพิมพ์ใหญ่กับคำที่เฉพาะเจาะจงในข้อความถอดเสียง ร่วมกับเครื่องหมายวรรคตอนและแท็กเสียงในบรรทัด เพื่อเน้นเสียงอย่างเป็นธรรมชาติในคำสำคัญ

This is a VERY important point!
It was a VERY long day <sigh> ... nobody listens anymore.

เสียงพูดและเสียงที่ไม่ใช่เสียงพูด

วางเสียงร้องของมนุษย์ที่ไม่ใช่คำพูดในบรรทัดโดยใช้วงเล็บมุม (<...>) ที่ จุดที่ควรมีเสียง แท็กเสียงร้องที่แนะนำมีดังนี้

<argh> <breath> <heavy breath> <exhales>
<cackle> <cheer> <chuckle> / <chuckles> <cough>
<cry> <gasp> <giggle> <groan>
<growl> <grunt> <grr> <hiss>
<laugh> / <laughter> <moan> <pant> <pff> / <phew>
<scream> <shout> <shriek> <sigh> / <sighs>
<sneeze> <snicker> <snort> <sob>
<throat-clearing> <tsk> <whimper> <whispers> / <whispering>
<yawn> <short pause> <long pause>

การตอบรับระหว่างการสนทนาและเสียงพูดทับซ้อนกัน

ในการสนทนาที่มีผู้พูดหลายคน ให้ใส่ปฏิกิริยาของผู้ฟังในอักขระไปป์ (|reaction|) ภายในเทิร์นของผู้พูดเพื่อสร้างช่องทางย้อนกลับที่เป็นธรรมชาติหรือ การพูดทับกันโดยไม่ต้องแบ่งเป็นเทิร์นแยกต่างหากต่อรีแอ็กชัน

  • การแลกเปลี่ยนข้อความสั้นๆ ในช่องสื่อสารเบื้องหลัง: แทรกรีแอ็กชันสั้นๆ ของผู้ฟัง (|oh hmm|, |oh really?|, |absolutely|) ในช่วงที่ผู้ที่กำลังพูดกำลังพูด
    • เทิร์นที่ 1 (ผู้พูด ก.): "So the launch is Thursday |oh hmm| Are we actually ready?"
    • ผลัดที่ 2 (ผู้พูด ข): "Ready enough |oh really?| The last blocker cleared this morning."
    • เทิร์นที่ 3 (ผู้พูด ก.): "Then let's ship it |absolutely| and watch the dashboards."
  • คำพูดที่ทับซ้อนกันและสลับกัน:ใช้ส่วนไปป์หลายส่วนเพื่อ จำลองคำพูดที่พูดพร้อมกันหรือสลับกันระหว่างผู้พูด 2 คน (ทำงานได้ดีที่สุด กับ gemini-3.8-flash-tts):
    • การนับถอยหลัง/การร้องประสานเสียงพร้อมกัน: "Let's surprise him on three |ok| ready?" ตามด้วย "one. two. three. |happy| happy |birthday| birthday!"
    • เสียงพูดทับกันโดยสมบูรณ์: "Hello |oh| there |my| it |goodness| must |gracious| be |would| almost |you| time |look| for |at that| dinner"

ความสอดคล้องกันในแต่ละรุ่นและสิ่งที่ควรหลีกเลี่ยง

ทำตามหลักเกณฑ์ต่อไปนี้เพื่อให้เสียงพูดมีความสม่ำเสมอในแต่ละรอบ

  • ออกแบบเพอร์โซนาตั้งแต่เนิ่นๆ ในการออกแบบเสียงแทนที่จะใช้บล็อกรูปแบบยาว: "Audio Profile" ย่อหน้าแบบยาวและ"Director's Notes" รายการแบบหลายหัวข้อย่อยที่นำมาจากโมเดลก่อนหน้านี้เป็นสาเหตุที่พบบ่อยที่สุดที่ทำให้เสียงเปลี่ยนไป ใช้สัญชาตญาณด้านครีเอทีฟโฆษณาเดียวกันนั้นตั้งแต่ต้นในการออกแบบเสียงเพื่อสร้างvoice_...ลักษณะตัวละครที่กำหนดเองแบบถาวร จากนั้นใช้รหัสเสียงนั้นผ่านการเรียก TTS
  • ใช้การอ้างอิงเสียงเพื่อความเสถียร (ละเว้นคำสั่งเมตา): โมเดล TTS ของ Gemini 3.8 ได้รับการฝึกให้ยึดการอ้างอิงเสียงเป็นอันดับแรก อย่าใส่วิธีการที่บอกโมเดลให้คงเสียงไว้ (เช่น "do not switch speaker identity" หรือ "maintain identical timbre") เพราะข้อความพรอมต์เพิ่มเติมจะทำให้เกิดการดริฟต์มากขึ้น ละเว้นคำสั่งสไตล์ที่ไม่จำเป็นและปล่อยให้โมเดล เปลี่ยนแปลงไปตามธรรมชาติรอบจุดที่เสถียรซึ่งอ้างอิงจากเสียง
  • อย่าพยายามเปลี่ยนลักษณะของผู้พูดที่เปลี่ยนแปลงไม่ได้ใน style: หลีกเลี่ยงการใส่ อายุ เพศ ชื่อ หรือการเปลี่ยนสำเนียงถาวรใน speech_metadata.style แต่ให้เลือกเสียงในภูมิภาคจากคลังเสียงเพิ่มเติมหรือสร้างเสียงด้วยการออกแบบเสียงแทน
  1. สร้างตัวละครเพียงครั้งเดียว: สร้างตัวละครในการออกแบบเสียงหรือเลือกเสียงในภูมิภาคจาก คลังเสียงเพิ่มเติมที่ตรงกับภาษาเป้าหมายและลักษณะตัวละคร
  2. เขียนข้อความถอดเสียงที่พูดอย่างเป็นธรรมชาติโดยมีคำพูดที่ไม่ต่อเนื่อง: เพื่อให้เป็นธรรมชาติมากที่สุด ให้เขียนtextเป็นข้อความถอดเสียงที่พูดจริง ซึ่งรวมถึงคำพูดที่ไม่ต่อเนื่องและการลังเลในการสนทนาตามธรรมชาติ (เช่น "Oh uh yeah I think... hm, so that's interesting")
  3. ทดสอบ TTS แบบธรรมดาก่อน: สังเคราะห์ข้อความถอดเสียงโดยใช้ฟิลด์ style ที่ว่างเปล่าก่อน เนื่องจากคำขอส่วนใหญ่ไม่จำเป็นต้องมีคำสั่ง style เลย
  4. เพิ่มพรอมต์สั้นๆ styleสำหรับการปรับแต่งเท่านั้น: เพิ่มstyleสตริง แบบย่อ (เช่น "casual, friendly" หรือ "muttering, then reassuring") เฉพาะสำหรับ เทิร์นที่ต้องมีการปรับการนำส่งที่เฉพาะเจาะจง และนำสตริงสั้นๆ ที่แน่นอนนั้นกลับมาใช้ซ้ำในเทิร์นต่างๆ เมื่อต้องการใช้ค่าพื้นฐานที่สอดคล้องกัน

ตัวแทนการสนทนาไปมาและเสียงพูดแบบหลายรอบ

เมื่อสร้างเอเจนต์เสียงแบบสนทนาแบบเรียลไทม์หรือแอปพลิเคชันแบบการสนทนาไปมา ให้ทำดังนี้

  • เรียกใช้ TTS 1 ครั้งต่อรอบเมื่อได้รับข้อความ LLM
  • ปล่อยให้ voice ที่กำหนดค่าไว้ (สร้างไว้ล่วงหน้า ออกแบบ voice_... หรือจำลอง voice_... / voicekey_...) ระบุตัวตนของลำโพงในแต่ละรอบ ไม่ต้องส่งตัวตนของตัวละครที่มีลักษณะยาวอีกในแต่ละรอบ
  • ปล่อยช่อง style ต่อรอบว่างไว้ หรือส่งสตริงค่าคงที่แบบสั้น 1 รายการ (เช่น "casual, friendly") สำหรับการสนทนาทั้งหมด
  • แบ่งคำตอบของตัวแทนที่ยาวออกเป็นคำตอบที่สั้นลงแทนที่จะใช้ พรอมต์สไตล์ที่เข้มข้นขึ้น

ข้อจำกัด

  • โมเดล TTS รับอินพุตที่เป็นข้อความเท่านั้นและสร้างเอาต์พุตที่เป็นเสียงเท่านั้น
  • การสร้างเสียงผู้พูดหลายคนในคำขอเดียว (multiSpeakerVoiceConfig / ผู้พูดหลายคน speakers) รองรับผู้พูดสูงสุด 2 คนโดยใช้เสียงที่สร้างไว้ล่วงหน้า หากต้องการรวมเสียงที่ออกแบบเอง (voice_...) หรือจำลอง (voicekey_...) ในบทสนทนาที่มีหลายตัวละคร ให้สังเคราะห์เสียงของแต่ละตัวละคร แยกกัน แล้วต่อเฟรมเสียง PCM 24kHz เข้าด้วยกัน
  • ขีดจำกัดพื้นที่เก็บข้อมูลเสียงที่กำหนดเองและ TTL:
    • เสียงแบบมีสถานะ (store=True, พรอมต์หรือจำลอง): สูงสุด 200 เสียงต่อโปรเจ็กต์ที่มี TTL 1 ปี (Time to Live)
    • คีย์เสียงแบบไม่เก็บสถานะ (store=False, voicekey_...): TTL 7 วัน (Time to Live)
  • ดูความครอบคลุมของภาษาในส่วนภาษาที่รองรับ

ขั้นตอนถัดไป