Voice design

Voice design

Voice design를 이용하면 Gemini API Voices 엔드포인트(POST /v1beta/voices)로 자연어 설명에서 완전히 새로운 지속적 음성 페르소나를 만들 수 있어요. 미리 만들어진 음성이나 참조 오디오 녹음에 제한되기보다, 캐릭터의 나이·음색·억양·기본 발화 방식을 설명하면 재사용 가능한 voice_... ID를 받아 프로젝트에 저장할 수 있어요.

커스텀 음성을 설계·오디션·반복하는 가장 빠른 방법은 Google AI Studio의 대화형 Voice Design 스튜디오예요. 텍스트 프롬프트로 커스텀 페르소나를 생성하고, 샘플 스크립트로 테스트하고, 결과 voice_... ID를 애플리케이션 코드에 바로 복사할 수 있어요.

Google AI Studio에서 사용해 보기

Gemini 3.8 Flash TTS(gemini-3.8-flash-tts)와 Gemini 3.8 Flash-Lite TTS(gemini-3.8-flash-lite-tts) 둘 다 Voice design을 지원해요.

출처: 원문

본문

설계한 음성 만들기

Google GenAI SDK(google-genai 2.25.0+ / @google/genai 2.24.0+)나 REST API로 텍스트 설명에서 커스텀 음성을 만들어요. "prompted" 음성의 경우 voices.create(CreateVoice)와 voices.get(GetVoice) 둘 다 출력 전용 sample_audio 필드(mime_type: "audio/wav", base64 인코딩된 data)를 반환해 생성된 음성을 바로 오디션할 수 있게 해 줘요.

import base64
from google import genai

client = genai.Client()

# 1. Design a custom voice persona from natural language
created_voice = client.voices.create(
    store=True,
    voice={
        "model": "gemini-3.8-flash-tts",
        "type": "prompted",
        "display_name": "Warm British Astronomer",
        "gender": "male",
        "language_code": "en-GB",
        "prompted": {
            "input": (
                "A warm, thoughtful astronomer in his late 60s with a gentle"
                " British accent, speaking with quiet wonder."
            )
        },
    },
)

print(f"Created voice ID: {created_voice.id}")

# Save the generated sample_audio preview (audio/wav) returned by CreateVoice
if created_voice.sample_audio and created_voice.sample_audio.data:
    with open("voice_preview.wav", "wb") as f:
        f.write(base64.b64decode(created_voice.sample_audio.data))
import * as fs from "node:fs";
import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI();

// 1. Design a custom voice persona from natural language
const createdVoice = await ai.voices.create({
  store: true,
  voice: {
    model: "gemini-3.8-flash-tts",
    type: "prompted",
    display_name: "Warm British Astronomer",
    gender: "male",
    language_code: "en-GB",
    prompted: {
      input:
        "A warm, thoughtful astronomer in his late 60s with a gentle British accent, speaking with quiet wonder.",
    },
  },
});

console.log(`Created voice ID: ${createdVoice.id}`);

// Save the generated sample_audio preview (audio/wav) returned by CreateVoice
if (createdVoice.sample_audio?.data) {
  fs.writeFileSync(
    "voice_preview.wav",
    Buffer.from(createdVoice.sample_audio.data, "base64")
  );
}
curl "https://generativelanguage.googleapis.com/v1beta/voices" \
  -H "x-goog-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "store": true,
    "voice": {
      "model": "gemini-3.8-flash-tts",
      "type": "prompted",
      "display_name": "Warm British Astronomer",
      "gender": "male",
      "language_code": "en-GB",
      "prompted": {
        "input": "A warm, thoughtful astronomer in his late 60s with a gentle British accent, speaking with quiet wonder."
      }
    }
  }' | tee created_voice.json | jq -r '.sample_audio.data' | base64 --decode > voice_preview.wav

Voice design의 작동 방식

  1. prompted 음성 만들기: type="prompted"와 store=True로 voices.create(POST /v1beta/voices)를 호출해요.
  2. 영구 voice_id와 sample_audio 프리뷰 받기: API가 음성 정체성을 생성해 프로젝트에 저장하고, 영구 ID(예: voice_abc123...)와 함께 생성된 프리뷰 오디오를 담은 sample_audio(mime_type: "audio/wav", base64 인코딩 data)를 반환해요.
  3. 음성 합성하기: 합성 요청에서 음성 이름이 허용되는 곳 어디든 voice_id를 전달해요.

설계한 음성으로 음성 합성하기

음성을 만든 뒤에는 그 id(voice_...)를 Interactions API에 전달해 음성을 생성해요.

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": (
                "Look out past the rings of Saturn. Those faint photons left"
                " their source millions of years ago."
            ),
            "annotations": [{
                "type": "speech_metadata",
                "style": "reflective and awe-inspired",
            }],
        }],
    }],
    response_format={"type": "audio"},
    generation_config={
        "speech_config": [
            {"voice": created_voice.id},
        ]
    },
)

with open("designed_voice.wav", "wb") as f:
    f.write(base64.b64decode(interaction.output_audio.data))
import * as fs from "node:fs";
import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI();

const interaction = await ai.interactions.create({
  model: "gemini-3.8-flash-tts",
  input: [{
    type: "user_input",
    content: [{
      type: "text",
      text: "Look out past the rings of Saturn. Those faint photons left their source millions of years ago.",
      annotations: [{
        type: "speech_metadata",
        style: "reflective and awe-inspired",
      }],
    }],
  }],
  response_format: { type: "audio" },
  generation_config: {
    speech_config: [
      { voice: createdVoice.id },
    ],
  },
});

fs.writeFileSync("designed_voice.wav", Buffer.from(interaction.output_audio.data, "base64"));
curl "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "model": "gemini-3.8-flash-tts",
    "input": [{
      "type": "user_input",
      "content": [{
        "type": "text",
        "text": "Look out past the rings of Saturn. Those faint photons left their source millions of years ago.",
        "annotations": [{
          "type": "speech_metadata",
          "style": "reflective and awe-inspired"
        }]
      }]
    }],
    "response_format": {"type": "audio"},
    "generation_config": {
      "speech_config": [
        {"voice": "voice_YOUR_DESIGNED_VOICE_ID"}
      ]
    }
  }' | jq -r '[.steps[] | select(.type=="model_output") | .content[] | select(.type=="audio")] | last | .data' | base64 --decode > out.wav

음성 관리하기

저장된 음성은 언제든 Voices API로 나열·필터·검사·삭제할 수 있어요(모든 필터 파라미터는 Extended Voice Library and filtering 참고).

  • 저장 한도와 TTL: 상태 저장 음성(store=True, prompted와 replicated 음성에 공유)은 프로젝트당 200개 음성 한도와 1년 TTL(time-to-live)이 있어요.
  • sample_audio 가용성: voices.create()(CreateVoice)와 voices.get()(GetVoice)는 "prompted" 음성에 대해 sample_audio(mime_type: "audio/wav", base64 인코딩 data)를 채워요. 목록을 가볍게 유지하려고 voices.list()(ListVoices)는 sample_audio를 생략해요("replicated"와 "prebuilt" 음성에서는 sample_audio가 설정되지 않아요).
from google import genai

client = genai.Client()

# List stored prompted voices in your project filtered by language
response = client.voices.list(
    type_=["prompted"],
    language_code=["en-US", "en-GB"],
)
for voice in response.voices or []:
    print(voice.id, voice.display_name, voice.type)

# Retrieve a specific voice by ID
voice_details = client.voices.get(id=created_voice.id)

# Delete a stored custom voice
client.voices.delete(id=created_voice.id)
import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI();

// List stored prompted voices in your project filtered by language
const response = await ai.voices.list({
  type: ["prompted"],
  language_code: ["en-US", "en-GB"],
});
for (const voice of response.voices ?? []) {
  console.log(voice.id, voice.display_name, voice.type);
}

// Retrieve a specific voice by ID
const voiceDetails = await ai.voices.get(createdVoice.id);

// Delete a stored custom voice
await ai.voices.delete(createdVoice.id);
# List stored prompted voices filtered by language
curl -G "https://generativelanguage.googleapis.com/v1beta/voices" \
  -H "x-goog-api-key: YOUR_API_KEY" \
  --data-urlencode "type=prompted" \
  --data-urlencode "language_code=en-US" \
  --data-urlencode "language_code=en-GB"

# Retrieve a specific voice by ID
curl "https://generativelanguage.googleapis.com/v1beta/voices/voice_YOUR_DESIGNED_VOICE_ID" \
  -H "x-goog-api-key: YOUR_API_KEY"

# Delete a stored custom voice
curl -X DELETE "https://generativelanguage.googleapis.com/v1beta/voices/voice_YOUR_DESIGNED_VOICE_ID" \
  -H "x-goog-api-key: YOUR_API_KEY"

Voice design 프롬프팅 모범 사례

  • 영구 음성 특성은 Voice design에, 스타일은 넣지 않기: 나이·성별·음색·목소리 질감·지역 억양 같은 불변 특성은 voices.create에서 음성을 만들 때 정의해요.
  • 상황별 감정은 speech_metadata.style에 맡기기: 커스텀 음성을 만든 뒤에는 "whispered urgently"나 "cheerful and energetic" 같은 짧은 style 프롬프트로 턴별 연기를 이끌되 화자의 핵심 정체성은 바꾸지 마세요.
  • 구체적이고 간결하게: 명확한 1~2문장 설명(예: "A crisp, energetic sports announcer in her 30s with a slight Midwestern accent")이 모순되거나 지나치게 긴 문단보다 더 깔끔하고 일관된 결과를 만들어요.

다음 단계

더 알아보기 (Learn more)