Generación de texto a voz (TTS)

La API de Gemini puede transformar la entrada de texto en audio de uno o varios oradores con las capacidades de generación de texto a voz (TTS) de Gemini. La generación de texto a voz es controlable, lo que significa que puedes combinar metadatos de turnos estructurados (speech_metadata) y etiquetas vocales intercaladas para guiar el estilo, el acento, el ritmo y el tono del audio.

La capacidad de TTS difiere de la generación de voz que se proporciona a través de la API de Live, que está diseñada para audio interactivo y no estructurado, y entradas y salidas multimodales. Si bien la API de Live se destaca en contextos conversacionales dinámicos, la API de Gemini TTS está diseñada para situaciones que requieren una recitación de texto exacta con un control detallado sobre el estilo y el sonido, como la generación de podcasts o audiolibros.

En esta guía, se muestra cómo generar audio de un solo orador y de varios oradores a partir de texto con Gemini 3.8 Flash TTS (gemini-3.8-flash-tts) y Gemini 3.8 Flash-Lite TTS (gemini-3.8-flash-lite-tts).

Antes de comenzar

Asegúrate de usar un modelo de Gemini TTS que se encuentre en la sección Modelos compatibles. Para obtener resultados óptimos, revisa Cuándo usar cada modelo para seleccionar el mejor modelo para tu carga de trabajo.

Antes de comenzar a compilar, te recomendamos probar los modelos de Gemini TTS en AI Studio.

TTS de un solo orador

Para convertir texto en audio de un solo orador con los modelos de Gemini 3.8 TTS, pasa la transcripción literal en input, adjunta el diseño a nivel de turno con una anotación speech_metadata y configura tu voz en generation_config.speech_config. Puedes elegir una voz de las Opciones de voz prediseñadas, la biblioteca de voces extendida (GET /v1beta/voices), un ID de diseño de voz personalizado (voice_...) o un ID de replicación de voz (voice_... o voicekey_... sin estado opcional).

En este ejemplo, se guarda el audio de salida WAV predeterminado (audio/wav) del modelo directamente en un archivo:

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" }
      ]
    }
  }'

Puedes recuperar los datos de audio generados con la propiedad interaction.output_audio, que devuelve el último bloque de audio generado. Para obtener detalles sobre las propiedades de conveniencia, consulta la Descripción general de las interacciones.

TTS con varios interlocutores

Para el diálogo con varios interlocutores, configura dos interlocutores en speech_config.speakers y pasa cada turno como un elemento de texto independiente con una anotación speech_metadata que especifique el speaker y el style opcional a nivel del turno. Usa "mode": "conversational" para una cadencia natural de turnos:

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" }
        ]
      }
    }
  }'

Controla el estilo del habla con metadatos y etiquetas

Gemini 3.8 TTS trata el campo text estrictamente como una transcripción literal. Para controlar la entrega sin que se lean en voz alta las indicaciones de escena, divide tus instrucciones por alcance:

  • Publicación sostenida a nivel de la unidad de diálogo (speech_metadata.style): Coloca las emociones, el estilo de publicación, la prosodia, el ritmo y el volumen que se aplican a toda una unidad de diálogo en el campo style (por ejemplo, "style": "whispered urgently", "style": "out of breath" o "style": "warm and enthusiastic").
  • Eventos puntuales (etiquetas intercaladas): Coloca pausas o ráfagas vocales momentáneas que no sean de voz directamente dentro de la transcripción con corchetes angulares (por ejemplo, "Wait... <short pause> did you hear that? <sigh>" o "Excuse me <cough> as I was saying...").

Consulta la Guía de instrucciones para conocer las prácticas recomendadas integrales.

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
}

Generación de voz en vivo

Puedes transmitir el audio generado a medida que se sintetiza configurando stream: true. A diferencia de las solicitudes unarias (que devuelven un archivo WAV completo con un encabezado RIFF), las solicitudes de transmisión devuelven de forma predeterminada fragmentos de PCM lineal sin procesar de 16 bits con signo y little-endian (audio/l16, 24 kHz, mono) sin encabezado, de modo que los fragmentos de audio se puedan reproducir o concatenar de forma continua sin encabezados de contenedor.

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
  }'

Formatos de salida de audio

Los modelos de TTS de Gemini 3.8 usan diferentes formatos de audio predeterminados según si la solicitud es unaria o de transmisión:

  • Solicitudes unarias (stream=False): Devuelven audio WAV (audio/wav) completo con un encabezado RIFF estándar (PCM de 24 kHz, mono, little-endian firmado de 16 bits). Puedes guardar los bytes de audio decodificados directamente en un archivo .wav sin anteponer manualmente un encabezado WAV.
  • Solicitudes de transmisión (stream=True): De forma predeterminada, devuelven fragmentos sin encabezado de PCM lineal sin procesar (audio/l16) (PCM de 24 kHz, mono, 16 bits con signo little-endian) para que los fragmentos se puedan transmitir o concatenar de forma continua sin encabezados de contenedor en cada fragmento.

Para solicitar una codificación de audio o una frecuencia de muestreo diferentes, configura mime_type y el campo opcional sample_rate dentro de response_format:

Formato Valor mime_type Descripción
WAV (valor predeterminado unario) "audio/wav" Archivo WAV sin comprimir con un encabezado RIFF (PCM little-endian firmado de 16 bits, mono, 24 kHz predeterminado). Es el valor predeterminado para las solicitudes unarias.
PCM sin procesar (L16) (predeterminado para la transmisión) "audio/l16" Audio PCM lineal de 16 bits firmado little-endian y sin comprimir (24 kHz, mono). Es el valor predeterminado para las solicitudes de transmisión.
Mu-law "audio/mulaw" Audio codificado en ley-μ G.711 de 8 bits (de uso frecuente en sistemas de telefonía o IVR de América del Norte y Japón).
Ley A "audio/alaw" Audio codificado con la ley A de G.711 de 8 bits (de uso frecuente en los sistemas telefónicos europeos e internacionales).

También puedes especificar sample_rate en hercios (por ejemplo, 24000, 16000 o 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" }
      ]
    }
  }'

Opciones de voz

Gemini 3.8 TTS admite cuatro formas de seleccionar o crear voces:

  1. Voces de estudio prediseñadas: 30 voces seleccionadas que se enumeran en la siguiente tabla.
  2. Biblioteca de voces extendida: Cientos de voces adicionales en diferentes idiomas, acentos y arquetipos de personajes a los que se puede acceder con client.voices.list() (GET /v1beta/voices).
  3. Diseño de voz: Genera un personaje vocal personalizado a partir de una descripción en lenguaje natural en Google AI Studio o con POST /v1beta/voices (type="prompted", que devuelve un ID de voice_... persistente y una vista previa en WAV de sample_audio en CreateVoice y GetVoice).
  4. Replicación de voz: Replica la voz de un orador a partir de audio de referencia y consentimiento en Google AI Studio o con POST /v1beta/voices (type="replicated", store=True persistente de forma predeterminada o store=False opcional sin estado).

Límites y TTL de voz personalizados

Tipo de voz Modo de almacenamiento Cuota o límite Retención (TTL)
Voces con estado (voice_..., generadas a partir de instrucciones o replicadas) store=True 200 voces por proyecto (compartidas entre las voces replicadas y las creadas con instrucciones) 1 año
Claves de voz sin estado (voicekey_..., replicadas) store=False Administrado por el cliente 7 días

Voces prediseñadas

Zephyr: Brillante Puck: Optimista Charon: Informativa
Kore, Firme Fenrir: Excitabilidad Leda: Juvenil
Orus: Firme Aoede: Breezy Callirrhoe: Voz tranquila
Autonoe: Brillo Enceladus: Respiración Iapetus: Claro
Umbriel: Tranquilo Algieba: Suave Despina: Suave
Erinome: Despejado Algenib: Gravelly Rasalgethi: Informativa
Laomedeia: Optimista Achernar: Suave Alnilam: Firme
Schedar: Par Gacrux: Contenido para mayores Pulcherrima: Reenvío
Achird: Amigable Zubenelgenubi: Casual Vindemiatrix: Suave
Sadachbia: Animada Sadaltager: Conocimiento Sulafat: Cálida

Biblioteca de voces y filtrado extendidos

Además de las 30 voces de estudio destacadas en la tabla anterior, la Biblioteca de voces extendida proporciona cientos de voces adicionales en diferentes idiomas, acentos regionales, personajes y dominios. Puedes explorar, filtrar y escuchar de forma interactiva la Biblioteca de voces completa en Google AI Studio, o bien consultarla de forma programática con client.voices.list() (GET /v1beta/voices, con google-genai 2.25.0 o versiones posteriores / @google/genai 2.24.0 o versiones posteriores).

ListVoices devuelve tus voces almacenadas personalizadas (ordenadas de la más reciente a la más antigua) seguidas de las voces del catálogo prediseñadas que coinciden con tus criterios de filtro. Cuando se pasan varios valores para un filtro de lista, se devuelven las voces que coinciden con cualquier valor de ese filtro (OR), mientras que los parámetros de filtro distintos se combinan con AND:

Parámetro Tipo Descripción
language_code list[str] Etiquetas de idioma BCP-47 (por ejemplo, ["en-US", "en-GB"]). Se realiza una coincidencia exacta que no distingue mayúsculas de minúsculas.
region_code list[str] Códigos de región ISO 3166-1 alpha-2 o UN M.49 (por ejemplo, ["US", "GB"]).
accent list[str] Descriptores de acento regional (por ejemplo, ["American", "British"])
gender list[str] Presentación de género percibida ("female", "male" o "neutral").
pitch list[str] Clasificación del tono vocal ("low", "medium" o "high").
persona list[str] Arquetipo de personaje o persona vocal (por ejemplo, ["Warm, Friendly"], ["Narrator"])
contexts (context en REST) list[str] Dominio de uso óptimo (por ejemplo, ["Audiobook", "Conversational", "News"])
type (type_ en Python) list[str] Filtrar por fuente de voz: "prebuilt", "prompted" (Diseño de voz) o "replicated" (Replicación de voz)
search str La búsqueda de subcadenas de texto libre coincidió sin distinguir mayúsculas de minúsculas con display_name y description.
page_size int Cantidad máxima de voces que se devuelven por página (el valor predeterminado es 50 y el máximo es 1000).
page_token str Token de response.next_page_token para recuperar la siguiente página de resultados.

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"

Idiomas admitidos

Los modelos de TTS detectan automáticamente el idioma de entrada. Gemini 3.8 Flash TTS (gemini-3.8-flash-tts) admite 130 idiomas, y Gemini 3.8 Flash-Lite TTS (gemini-3.8-flash-lite-tts) admite 101 idiomas:

Idioma TTS de Gemini 3.8 Flash TTS de Gemini 3.8 Flash-Lite
Acehnés (escritura árabe) ✔️ ✔️
Afrikaans ✔️ ✔️
Akan ✔️ ✔️
Amárico ✔️ ✔️
Armenio ✔️ ✔️
Asamés ✔️ ✔️
Awadhi ✔️ ✔️
Balinés ✔️ ✔️
Bengalí ✔️ ✔️
Banjar (escritura árabe) ✔️
Banjar (alfabeto latino) ✔️ ✔️
Baskir ✔️
Euskara ✔️ ✔️
Bielorruso ✔️ ✔️
Bemba ✔️
Bhojpuri ✔️ ✔️
Bosnio ✔️ ✔️
Buginés ✔️ ✔️
Búlgaro ✔️ ✔️
Birmano ✔️
Cantonés ✔️ ✔️
Catalán ✔️ ✔️
Cebuano ✔️ ✔️
Kurdo ✔️ ✔️
Chatisgarí ✔️ ✔️
Chino (escritura Han) ✔️ ✔️
Chino (secuencia de comandos Hant) ✔️ ✔️
Tártaro de Crimea ✔️
Croata ✔️ ✔️
Checo ✔️ ✔️
Danés ✔️ ✔️
Holandés ✔️ ✔️
Diula ✔️
Dzongkha ✔️
Árabe egipcio ✔️ ✔️
Inglés ✔️ ✔️
Estonio ✔️ ✔️
Filipino ✔️ ✔️
Finlandés ✔️
Francés ✔️ ✔️
Gallego ✔️ ✔️
luganda ✔️ ✔️
Georgiano ✔️ ✔️
Alemán ✔️ ✔️
Griego ✔️ ✔️
Guaraní ✔️
Gujarati ✔️ ✔️
Criollo haitiano ✔️ ✔️
Mongol de Halh ✔️ ✔️
Hausa ✔️ ✔️
Hebreo ✔️ ✔️
Hindi ✔️ ✔️
Húngaro ✔️ ✔️
Islandés ✔️ ✔️
Igbo ✔️
Ilocano ✔️ ✔️
Indonesio ✔️ ✔️
Persa iraní ✔️ ✔️
Italiano ✔️ ✔️
Japonés ✔️ ✔️
Javanés ✔️ ✔️
Kabyle ✔️
Kamba ✔️ ✔️
Canarés ✔️ ✔️
Cachemir (alfabeto árabe) ✔️ ✔️
Cachemir (alfabeto devanagari) ✔️ ✔️
Kazajo ✔️ ✔️
Jemer ✔️ ✔️
Kikuyu ✔️ ✔️
Kiñarwanda ✔️ ✔️
Congo ✔️ ✔️
Coreano ✔️ ✔️
Kirguís ✔️ ✔️
Laosiano ✔️ ✔️
Latgaliano ✔️
Lingala ✔️ ✔️
Lituano ✔️
Luxemburgués ✔️
Macedonio ✔️ ✔️
Magahi ✔️ ✔️
Maithili ✔️ ✔️
Malayalam ✔️ ✔️
Maltés ✔️ ✔️
Manipuri ✔️ ✔️
Maratí ✔️ ✔️
Minangkabau (escritura árabe) ✔️ ✔️
Minangkabau (alfabeto latino) ✔️
Mizo ✔️ ✔️
Nepalí (idioma individual) ✔️ ✔️
Fulfulde nigeriano ✔️ ✔️
Azerí del norte ✔️ ✔️
Sotho norteño ✔️ ✔️
Uzbeko del norte ✔️ ✔️
Bokmål ✔️ ✔️
Noruego (Nynorsk) ✔️ ✔️
Nyanja ✔️ ✔️
Occitano ✔️
Oriya (idioma individual) ✔️ ✔️
Pangasinán ✔️
Persa (Afganistán) ✔️ ✔️
Polaco ✔️ ✔️
Portugués ✔️ ✔️
Punyabí ✔️ ✔️
Rumano ✔️ ✔️
Ruso ✔️ ✔️
Santali ✔️ ✔️
Serbio ✔️ ✔️
Sindhi ✔️
Cingalés ✔️ ✔️
Eslovaco ✔️ ✔️
Esloveno ✔️
Somalí ✔️
Azerí del sur ✔️ ✔️
Pastún meridional ✔️ ✔️
Sesoto meridional ✔️
Español ✔️ ✔️
Árabe estándar (alfabeto árabe) ✔️ ✔️
Árabe estándar (alfabeto latino) ✔️ ✔️
Letón estándar ✔️ ✔️
Malayo estándar ✔️ ✔️
Suajili (idioma individual) ✔️
Suazi ✔️
Sueco ✔️
Tayiko ✔️
Tamil ✔️ ✔️
Telugu ✔️ ✔️
Tailandés ✔️
Tigrinya ✔️
Albanés tosk ✔️
Uigur ✔️

Modelos compatibles

Modelo Orador único Varios oradores Diseño de voz Replicación de voz
TTS de Gemini 3.8 Flash (gemini-3.8-flash-tts) ✔️ ✔️ ✔️ ✔️
TTS de Gemini 3.8 Flash-Lite (gemini-3.8-flash-lite-tts) ✔️ ✔️ ✔️ ✔️
Versión preliminar del TTS de Gemini 3.1 Flash ✔️ ✔️
TTS de Gemini 2.5 Pro en versión preliminar ✔️ ✔️

Cuándo usar cada modelo

Ambos modelos de TTS de Gemini 3.8 comparten el mismo esquema de API y formato de instrucciones, lo que te permite cambiar entre ellos con un solo cambio de parámetro:

  • Usa Gemini 3.8 Flash TTS (gemini-3.8-flash-tts) cuando la máxima fidelidad acústica, la actuación matizada y el control expresivo sean la principal prioridad. Es ideal para trabajos creativos con calidad de estudio, diálogos complejos con varios oradores, etiquetas de ráfagas vocales intensas, pronunciaciones difíciles, dialectos regionales o minoritarios, y narraciones de formato largo que requieren una estabilidad sólida de la voz y el tono de la sala.
  • Usa Gemini 3.8 Flash-Lite TTS (gemini-3.8-flash-lite-tts) como tu herramienta de trabajo rápida y rentable en reemplazo de gemini-3.1-flash-tts-preview. Está optimizado para la producción masiva de gran volumen, las cascadas de agentes de voz conversacionales, las funciones de lectura en voz alta, la replicación de voz confiable y el habla cotidiana de un solo orador en los principales idiomas.

Guía de migración

Si migras desde modelos de Gemini TTS de gemini-3.1-flash-tts-preview o versiones anteriores a Gemini 3.8 TTS, haz lo siguiente:

  1. Mueve las instrucciones a nivel de turno a speech_metadata: Gemini 3.8 TTS trata el texto de entrada estrictamente como una transcripción literal. Traslada las instrucciones de entrega sostenida (style, como "whispering", "out of breath" o "speaking slowly") y las etiquetas de interlocutor (speaker) a las anotaciones estructuradas speech_metadata en lugar de incorporar las indicaciones de escena en el texto de la transcripción.
  2. Usa etiquetas intercaladas entre corchetes angulares solo para eventos vocales puntuales: Mantén las vocalizaciones y las pausas momentáneas que no sean de voz intercaladas en la transcripción con corchetes angulares (como <laugh>, <sigh>, <cough>, <breath> o <short pause>). Evita las etiquetas de efectos de sonido (como aplausos o golpes) y coloca los estilos de entrega en speech_metadata.style.
  3. Especifica speaker en cada turno de las solicitudes con varios interlocutores: Cada turno de una solicitud con varios interlocutores debe incluir de forma explícita speaker dentro de speech_metadata, que coincida con uno de los interlocutores configurados.
  4. Diseña arquetipos de usuarios por adelantado con el diseño de voz: Reemplaza los bloques de varios párrafos "Audio Profile" o "Director's Notes" por una voz personalizada creada en Diseño de voz y, luego, lleva ese ID de voice_... a través de tus solicitudes de TTS con cadenas de style mínimas o vacías.
  5. Ten en cuenta la salida WAV predeterminada (audio/wav) en las solicitudes unarias: A diferencia de gemini-3.1-flash-tts-preview y los modelos de TTS anteriores (que devolvían PCM sin procesar sin encabezado audio/l16 de forma predeterminada), Gemini 3.8 TTS devuelve audio WAV (audio/wav) con un encabezado RIFF estándar de forma predeterminada para las solicitudes unarias.
    • Si tu código anteriormente encapsulaba bytes de PCM sin procesar en un encabezado WAV (por ejemplo, con el módulo wave o ffmpeg de Python), quita el encapsulador de encabezado manual y escribe los bytes devueltos directamente en un archivo .wav.
    • Si tu canalización requiere audio PCM sin procesar, mu-law o A-law sin encabezado, configura response_format de forma explícita como "audio/l16", "audio/mulaw" o "audio/alaw". Consulta Formatos de salida de audio.

Guía de instrucciones

Los modelos de Gemini 3.8 TTS tratan el texto de entrada estrictamente como una transcripción literal. A diferencia de los modelos de vista previa anteriores, en los que las indicaciones de escena se incorporaban en texto sin formato, el TTS de Gemini 3.8 separa las indicaciones sostenidas a nivel de turno (speech_metadata) de las etiquetas vocales intercaladas en un momento determinado.

Campo de estilo en comparación con las etiquetas intercaladas

Divide tus instrucciones de rendimiento por alcance:

  • Entrega a nivel de turno (speech_metadata.style): Coloca atributos de entrega sostenida, como emoción, prosodia, ritmo general o estilo de entrega (como "whispering", "out of breath", "muttering" o "sarcastic"), en el campo style de speech_metadata. Para crear un personaje y un rendimiento estables en cada turno, diseña el arquetipo por adelantado en Diseño de voz y usa style solo para ajustes opcionales a nivel del turno.
  • Eventos puntuales (etiquetas intercaladas): Coloca ráfagas vocales momentáneas que no sean de voz, respiraciones o pausas intercaladas en la transcripción con corchetes angulares (<cough>, <breath>, <sigh>, <short pause>). Usa corchetes angulares (<...>) para obtener la calidad de audio más alta y limítate a las vocalizaciones humanas en lugar de los efectos de sonido no vocales.
Alcance Dónde colocarlo Ejemplos
A nivel del turno (se mantiene durante el turno) speech_metadata.style "angry tone", "speaking rapidly", "out of breath", "whispers" y "sarcastic"
En un momento determinado (ocurre en una palabra específica) En línea en text (<...>) "<cough> Thank you all for coming tonight! <throat-clearing> As I was saying..."

Ritmo y pausas

Puedes controlar el ritmo y el silencio en tres niveles de detalle:

  • Puntuación y puntos suspensivos: Usa comas, guiones (--) y puntos suspensivos (...) para generar una vacilación natural en la conversación.
  • Etiquetas de pausa intercaladas: Inserta <short pause> o <long pause> en los puntos exactos del guion en los que un orador debería hacer una pausa: text Hold on, let me think... <short pause> Alright, I've got it.
  • Ritmo a nivel del turno: Establece "style": "speaking rapidly" o "style": "speaking slowly" en speech_metadata para controlar la velocidad de habla en todo el turno.

Prosodia y tono

Usa speech_metadata.style para controlar la prosodia, el tono y la inflexión en un turno (por ejemplo, "style": "high pitch, cheerful and excited inflection" o "style": "monotone and flat"). Si la emoción o la prosodia cambian a mitad del diálogo, divide el guion en turnos separados con valores de style distintos para cada turno.

Énfasis

Usa mayúsculas en palabras específicas de la transcripción, junto con signos de puntuación y etiquetas vocales intercaladas, para enfatizar naturalmente las palabras clave:

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

Explosiones vocales y sonidos no verbales

Coloca las vocalizaciones humanas que no sean de voz en línea con corchetes angulares (<...>) en el punto exacto en el que debería ocurrir el sonido. Entre las etiquetas vocales recomendadas, se incluyen las siguientes:

<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>

Canales secundarios y voces superpuestas

En el diálogo con varios oradores, incluye las reacciones del interlocutor entre caracteres de barra vertical (|reaction|) dentro del turno de un orador para crear canales secundarios naturales o superponer el habla sin interrumpir el turno de cada reacción.

  • Intercambios breves en el canal secundario: Agrega reacciones breves del público (|oh hmm|, |oh really?|, |absolutely|) durante el turno del orador activo:
    • Turno 1 (orador A): "So the launch is Thursday |oh hmm| Are we actually ready?"
    • Turno 2 (orador B): "Ready enough |oh really?| The last blocker cleared this morning."
    • Turn 3 (Speaker A): "Then let's ship it |absolutely| and watch the dashboards."
  • Superposición y entrelazado del habla: Usa varios segmentos de barra vertical para simular el habla simultánea o entrelazada entre dos oradores (funciona mejor con gemini-3.8-flash-tts):
    • Cuenta regresiva o coro simultáneos: "Let's surprise him on three |ok| ready?" seguido de "one. two. three. |happy| happy |birthday| birthday!"
    • Superposición total de oradores: "Hello |oh| there |my| it |goodness| must |gracious| be |would| almost |you| time |look| for |at that| dinner"

Coherencia entre las generaciones y qué evitar

Sigue estos lineamientos para mantener la estabilidad de la identidad vocal en los diferentes turnos:

  • Diseña arquetipos por adelantado en el diseño de voz en lugar de usar largos bloques de estilo: Los párrafos de formato largo "Audio Profile" y las listas con varios viñetas "Director's Notes" que se transfirieron de modelos anteriores son la causa más común de la desviación de voz. Usa esa misma intuición creativa desde el principio en Diseño de voz para generar un personaje voice_... personalizado persistente y, luego, lleva ese ID de voz a través de tus llamadas de TTS.
  • Confía en la referencia de voz para la estabilidad (omite las metainstrucciones): Los modelos de TTS de Gemini 3.8 se entrenan para anclarse primero en la referencia de audio. No incluyas instrucciones que le indiquen al modelo que mantenga la voz estable (como "do not switch speaker identity" o "maintain identical timbre"): el texto adicional de la instrucción aumenta la desviación. Descarta las instrucciones de estilo innecesarias y permite que el modelo varíe de forma natural en torno al punto estable que proporciona la referencia de voz.
  • No intentes cambiar los rasgos inmutables del orador en style: Evita incluir cambios de edad, género, nombres o acento permanentes en speech_metadata.style. En su lugar, elige una voz regional de la Biblioteca de voces extendida o crea una con Diseño de voz.
  1. Crea el personaje una sola vez: Crea tu personaje en Diseño de voz o selecciona una voz regional de la Biblioteca de voces extendida que coincida con tu idioma y arquetipo objetivo.
  2. Escribe transcripciones habladas naturales con disfluencias: Para lograr la máxima naturalidad, escribe la text como una transcripción hablada real, incluidas las disfluencias y las vacilaciones naturales de la conversación (por ejemplo, "Oh uh yeah I think... hm, so that's interesting").
  3. Primero, prueba el TTS simple: Sintetiza tu transcripción con un campo style vacío primero. La mayoría de las solicitudes no necesitan ninguna instrucción style.
  4. Agrega instrucciones breves de style solo para ajustes: Agrega una cadena de style concisa (como "casual, friendly" o "muttering, then reassuring") solo para los turnos que necesiten un ajuste de entrega específico y reutiliza esa misma cadena breve en todos los turnos cuando desees un modelo de referencia coherente.

Agentes de voz y diálogo de varios turnos

Cuando compiles agentes de voz conversacionales en tiempo real o aplicaciones de varios turnos, ten en cuenta lo siguiente:

  • Realiza una llamada de TTS por turno a medida que llegan los fragmentos de texto del LLM.
  • Permite que el voice configurado (prediseñado, voice_... diseñado o voice_... / voicekey_... replicado) transmita la identidad del orador en cada turno. Nunca vuelvas a enviar un personaje largo en cada turno.
  • Deja el campo style por turno vacío o envía una cadena constante corta (como "casual, friendly") para toda la conversación.
  • Divide las respuestas largas de los agentes en turnos más cortos en lugar de usar instrucciones de estilo más sólidas.

Limitaciones

  • Los modelos de TTS aceptan entradas solo de texto y generan salidas solo de audio.
  • La generación de varios interlocutores con una sola solicitud (multiSpeakerVoiceConfig / multi-speaker speakers) admite hasta 2 interlocutores con voces prediseñadas. Para combinar voces diseñadas de forma personalizada (voice_...) o replicadas (voicekey_...) en diálogos con varios personajes, sintetiza el turno de cada orador de forma individual y concatena los fotogramas de audio PCM de 24 kHz.
  • Límites de almacenamiento y TTL de voz personalizados:
    • Voces con estado (store=True, replicadas o con instrucciones): Máximo de 200 voces por proyecto con un TTL de 1 año (tiempo de actividad).
    • Claves de voz sin estado (store=False, voicekey_...): TTL de 7 días (tiempo de actividad).
  • Revisa la sección Idiomas admitidos para conocer la cobertura de idiomas.

¿Qué sigue?