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 วิธีในการเลือกหรือสร้างเสียง ดังนี้
- เสียงสตูดิโอที่สร้างไว้ล่วงหน้า: เสียงที่คัดสรรมา 30 เสียงซึ่งแสดงอยู่ในตารางต่อไปนี้
- คลังเสียงเพิ่มเติม: เสียงเพิ่มเติมอีกหลายร้อยเสียงในภาษา
สำเนียง และต้นแบบตัวละครต่างๆ ซึ่งเข้าถึงได้โดยใช้
client.voices.list()(GET /v1beta/voices) - การออกแบบเสียง: สร้างลักษณะเสียงร้องที่กำหนดเองจากคำอธิบายภาษาธรรมชาติใน Google AI Studio หรือใช้
POST /v1beta/voices(type="prompted"ซึ่งจะแสดงรหัสvoice_...ที่คงอยู่และตัวอย่างsample_audioWAV ในCreateVoiceและGetVoice) - การจำลองเสียง: จำลองเสียงของ
ผู้พูดจากเสียงอ้างอิงและเสียงที่ได้รับความยินยอมใน
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 ให้ทำดังนี้
- ย้ายคำสั่งระดับเลี้ยวไปยัง
speech_metadata: TTS ของ Gemini 3.8 จะถือว่า ข้อความที่ป้อนเป็นสำเนาที่ตรงตามต้นฉบับอย่างเคร่งครัด ย้ายวิธีการนำส่งที่ต่อเนื่อง (styleเช่น"whispering","out of breath"หรือ"speaking slowly") และป้ายกำกับผู้พูด (speaker) ไปยังคำอธิบายประกอบที่มีโครงสร้างspeech_metadataแทนที่จะฝังคำแนะนำบนเวทีในข้อความ ถอดเสียง - ใช้แท็กในบรรทัดที่มีเครื่องหมายวงเล็บปีกกาสำหรับเหตุการณ์เสียงร้อง ณ จุดใดจุดหนึ่งเท่านั้น: เก็บเสียงร้องที่ไม่ใช่คำพูดและหยุดชั่วคราวในบรรทัดในข้อความถอดเสียงโดยใช้เครื่องหมายวงเล็บปีกกา (เช่น
<laugh>,<sigh>,<cough>,<breath>หรือ<short pause>) หลีกเลี่ยงแท็กเอฟเฟกต์เสียง (เช่น เสียงปรบมือหรือเสียงทุ้ม) และใส่รูปแบบการนำเสนอในspeech_metadata.style - ระบุ
speakerในทุกๆ เทิร์นในคำขอแบบหลายผู้พูด: ทุกๆ เทิร์นในคำขอแบบหลายผู้พูดต้องมีspeakerอยู่ภายในspeech_metadataที่ตรงกับผู้พูดที่กำหนดค่าไว้ - ออกแบบเพอร์โซนาล่วงหน้าด้วยการออกแบบเสียง: แทนที่บล็อกหลายย่อหน้า
"Audio Profile"หรือ"Director's Notes"ด้วยเสียงที่กำหนดเองซึ่งสร้างขึ้น ในการออกแบบเสียง จากนั้นส่งvoice_...รหัสนั้นผ่านคำขอ TTS โดยมีสตริงstyleที่ว่างเปล่าหรือมีข้อมูลน้อยที่สุด - พิจารณาเอาต์พุต 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"อย่างชัดเจน ดูรูปแบบเอาต์พุตเสียง
- หากก่อนหน้านี้โค้ดของคุณห่อไบต์ PCM ดิบไว้ในส่วนหัวของ WAV (เช่น ใช้โมดูล
คำแนะนำในการเขียนพรอมต์
โมเดล 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."
- เทิร์นที่ 1 (ผู้พูด ก.):
- คำพูดที่ทับซ้อนกันและสลับกัน:ใช้ส่วนไปป์หลายส่วนเพื่อ
จำลองคำพูดที่พูดพร้อมกันหรือสลับกันระหว่างผู้พูด 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แต่ให้เลือกเสียงในภูมิภาคจากคลังเสียงเพิ่มเติมหรือสร้างเสียงด้วยการออกแบบเสียงแทน
เวิร์กโฟลว์ที่แนะนำ
- สร้างตัวละครเพียงครั้งเดียว: สร้างตัวละครในการออกแบบเสียงหรือเลือกเสียงในภูมิภาคจาก คลังเสียงเพิ่มเติมที่ตรงกับภาษาเป้าหมายและลักษณะตัวละคร
- เขียนข้อความถอดเสียงที่พูดอย่างเป็นธรรมชาติโดยมีคำพูดที่ไม่ต่อเนื่อง: เพื่อให้เป็นธรรมชาติมากที่สุด ให้เขียน
textเป็นข้อความถอดเสียงที่พูดจริง ซึ่งรวมถึงคำพูดที่ไม่ต่อเนื่องและการลังเลในการสนทนาตามธรรมชาติ (เช่น"Oh uh yeah I think... hm, so that's interesting") - ทดสอบ TTS แบบธรรมดาก่อน: สังเคราะห์ข้อความถอดเสียงโดยใช้ฟิลด์
styleที่ว่างเปล่าก่อน เนื่องจากคำขอส่วนใหญ่ไม่จำเป็นต้องมีคำสั่งstyleเลย - เพิ่มพรอมต์สั้นๆ
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)
- เสียงแบบมีสถานะ (
- ดูความครอบคลุมของภาษาในส่วนภาษาที่รองรับ
ขั้นตอนถัดไป
- สร้างตัวตนของเสียงร้องที่กำหนดเองจากภาษาธรรมชาติด้วยการออกแบบเสียง
- จำลองเสียงของวิทยากรที่มีอยู่แล้วในการจำลองเสียง
- เปรียบเทียบข้อกำหนดของโมเดลในหน้าโมเดล Gemini 3.8 Flash TTS และ Gemini 3.8 Flash-Lite TTS
- สำรวจเสียงแบบโต้ตอบ 2 ทิศทางด้วย Live API