모든 Telnyx 음성을 한 장면에서 듣고 싶어서 멀티 캐릭터 내레이터를 만들었습니다
요약
Telnyx의 Ultra 음성 API를 활용하여 여러 캐릭터의 목소리와 감정을 하나의 오디오 파일로 렌더링하는 멀티 캐릭터 내레이터 앱 개발 사례를 소개합니다. 단순 샘플 재생을 넘어 SSML을 통해 감정, 속도, 문맥을 반영한 실제 대화 환경을 구현하는 방법을 다룹니다.
핵심 포인트
- Telnyx Ultra 음성을 활용한 멀티 캐릭터 오디오 렌더링 구현
- SSML 감정 태그를 통한 캐릭터별 감정 및 속도 제어
- 병렬 TTS 호출 및 오디오 결합을 통한 연속된 MP3 생성 프로세스
- 단일 샘플 문장의 한계를 극복하는 문맥 중심의 음성 테스트 방법
Telnyx는 36개 언어에 걸쳐 100ms 미만의 첫 바이트 시간(time-to-first-byte)을 가진 700개 이상의 Ultra 음성을 제공합니다. 문제는 음성이 아닙니다. 그 음성들을 듣는 방식이 문제입니다.
문서에는 세 가지가 나열되어 있습니다. Voices API는 모든 제공업체에 걸쳐 4,000개 이상의 음성을 반환합니다. 음성 선택기(Voice pickers)는 각 음성당 고정된 샘플 문장을 재생합니다. 그 중 어느 것도 실제 장면 안에서 음성이 감정, 속도(pacing), 또는 캐릭터를 어떻게 처리하는지 알려주지 않습니다.
그래서 저는 정확히 그 작업을 수행할 수 있는 작은 앱을 만들었습니다. 몇 명의 캐릭터가 등장하는 짧은 장면을 작성하고, 각 캐릭터에 서로 다른 Telnyx Ultra 음성과 SSML 감정을 할당한 다음, 전체를 하나의 MP3로 렌더링합니다. 모든 음성은 하나의 연속된 오디오 파일 안에서 캐릭터에 맞춰, 문맥에 맞게 말합니다.
Telnyx 코드 예제는 다음과 같습니다:
https://github.com/team-telnyx/telnyx-code-examples/tree/main/multi-character-narrator-python
사용 사례 (The Use Case)
음성 선택기가 존재합니다. 이들은 각 음성당 고정된 샘플 문장을 재생합니다. 하지만 이들이 하지 못하는 것은 실제 장면 안에서 음성을 듣게 하는 것입니다. 즉, 긴박한 논쟁, 차분한 내레이터, 공황 상태의 캐릭터, 안심시키는 가이드 등 말이죠. 단 하나의 샘플 문장으로는 음성이 감정, 속도(pacing), 또는 캐릭터를 어떻게 처리하는지 알 수 없기 때문입니다.
이 예제는 그 문제를 해결합니다. 몇 명의 캐릭터가 등장하는 짧은 장면을 작성합니다. 각 캐릭터는 서로 다른 Telnyx Ultra 음성을 할당받습니다. 각 캐릭터는 SSML 감정을 할당받습니다. 그리고 렌더링을 누릅니다. 앱은 병렬 TTS 호출을 분산시키고, 스크립트 순서에 따라 라인별 오디오를 결합하여, 모든 음성이 캐릭터에 맞춰 말하는 하나의 연속된 MP3를 재생합니다.
기본 장면은 율리우스 카이사르(Julius Caesar)의 '3월의 이데(Ides of March)'입니다. 5명의 캐릭터, 10개의 라인, 5개의 뚜렷한 음성, 5가지의 서로 다른 감정:
- Cassius — 결연한, 암살을 모의하는
- Caesar — 놀란, 배신을 깨닫는
- Brutus — 사과하는, 행위를 정당화하는
- Mark Antony — 화가 난, 쓰러진 지도자를 애도하는
- Narrator — 차분한, 장면을 설정하는
한 번의 렌더링, 하나의 MP3, 문맥 속의 모든 음성. 이것이 데모입니다.
8가지 엄선된 Ultra 음성
이 앱은 가장 일반적인 사용 사례에 맞춰 엄선된 8가지 사전 구축된 Telnyx Ultra 음성을 제공합니다. 각 음성은 REST 엔드포인트에서 작동하는 UUID 음성 ID를 가진 실제 Telnyx 음성입니다.
| 음성 (Voice) | 성별 (Gender) | 언어 (Language) | 최적의 사용 사례 (Best Use Case) | 사운드 프로필 (Sound Profile) |
|---|---|---|---|---|
| Asher | 남성 | en | 음성 비서 및 미디어 | 부드럽고 역동적인 팟캐스터 스타일의 톤 |
| ... |
어떤 캐릭터든 8가지 중 하나를 선택할 수 있습니다. 전체 장면을 렌더링하기 전에 '미리보기 (Preview)'를 클릭하여 선택한 감정이 적용된 음성을 들어보세요.
20가지 Ultra SSML 감정
Ultra는 텍스트 앞에 배치되는 인라인 SSML 감정 태그를 지원합니다:
<emotion value="excited" />좋은 소식이에요 — 주문하신 상품이 조기 배송되었습니다!
앱은 20가지 Ultra SSML 감정 전체를 캐릭터별 드롭다운 메뉴로 제공합니다. 주요 감정: angry (화남), excited (흥분), content (만족), sad (슬픔), scared (무서움). 추가 감정: happy (행복), enthusiastic (열정적), curious (호기심), calm (차분), grateful (감사), affectionate (애정 어린), sarcastic (비꼬는), surprised (놀람), confident (자신감 있는), hesitant (망설이는), apologetic (사과하는), determined (단호한), frustrated (좌절한), disappointed (실망한).
기본 Julius Caesar 장면의 각 캐릭터에는 역할에 맞는 감정이 자동으로 할당됩니다. Cassius는 determined (단호함), Caesar는 surprised (놀람), Brutus는 apologetic (사과함), Mark Antony는 angry (화남), Narrator는 calm (차분함)입니다. 동일한 음성이지만 감정과 전달 방식이 다르며, 이 모든 것이 줄당 하나의 인라인 SSML 태그로 이루어집니다.
제작 과정
이 앱은 인라인 브라우저 UI를 포함한 단일 Flask 파일로 구성되어 있습니다. 전화번호, 웹훅 (webhook), 클라우드 스토리지 (Cloud Storage), 데이터베이스가 필요 없습니다. 단 하나의 환경 변수만 필요합니다: TELNYX_API_KEY.
파이프라인 (The pipeline)
POST /narrate (화자 라벨이 포함된 스크립트)
-> 스크립트를 순서대로 정렬된 라인으로 파싱 (parse)
-> 화자 -> 음성 매핑 (8가지 엄선된 Ultra 음성, 재정의 가능)
...
WebSocket이 아닌 REST를 사용한 이유
공용 WebSocket에서 Ultra는 REST 전용입니다. wss://api.telnyx.com/v2/text-to-speech/speech에서 발생하는 403 오류는 의도된 것입니다. 이 앱은 output_type: binary_output과 함께 POST /v2/text-to-speech/speech를 사용하여 라인당 실제 첫 번째 바이트 도달 시간 (time-to-first-byte)을 측정할 수 있도록 했습니다. Base64 모드는 실제 지연 시간 (latency)을 숨길 수 있기 때문입니다.
왜 디스플레이 이름이 아닌 UUID를 사용하는가
Ultra 음성 ID는 Telnyx.Ultra.Clara와 같은 짧은 디스플레이 이름이 아니라 Telnyx.Ultra.<uuid> 형식의 UUID입니다. 짧은 이름을 사용하면 REST 엔드포인트에서 400 에러가 반환됩니다. GET /v2/text-to-speech/voices 경로의 Voices API는 사용 가능한 모든 음성을 해당 UUID와 함께 반환합니다. Ultra 음성만 700개가 넘으며, provider == "telnyx" 및 id | startswith("Telnyx.Ultra.")를 통해 필터링할 수 있습니다.
데모가 즉시 작동할 수 있도록 앱에는 8개의 엄선된 UUID가 포함되어 있지만, 드롭다운 메뉴는 Voices API에서 가져온 어떤 음성으로도 쉽게 확장할 수 있습니다.
라인별 오류 격리를 통한 병렬 팬아웃 (Parallel fan-out)
이 앱은 ThreadPoolExecutor를 사용하여 모든 라인을 병렬로 렌더링합니다. 만약 한 라인이 실패하더라도 (예: 잘못된 음성 오버라이드), 응답에는 errors 배열이 포함되며, 결합된 오디오에는 스크립트 순서대로 성공한 라인들만 포함됩니다. 라인 하나가 실패한다고 해서 전체 렌더링을 놓치지는 않습니다.
브라우저 UI
UI는 타이핑하는 동안 화자를 자동으로 감지합니다. 각 화자에게는 음성 드롭다운, 감정 (emotion) 드롭다운, 그리고 선택한 음성과 감정으로 짧은 샘플 라인을 렌더링하는 미리보기 (Preview) 버튼이 제공됩니다. 렌더링 버튼을 누르면 병렬 TTS 호출이 팬아웃(fan-out)되어 실행되고, 결과가 결합(stitch)된 후 MP3가 자동 재생됩니다.
실행 방법
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/multi-character-narrator-python
cp .env.example .env # TELNYX_API_KEY를 입력하세요
...
브라우저 UI를 엽니다. 기본 Julius Caesar 스크립트가 미리 로드되어 있습니다. 음성을 선택하고, 감정을 선택하고, 미리보기를 하고, 렌더링한 뒤 재생하세요.
향후 발전 방향
이 앱은 시작점일 뿐입니다. Voices API를 통해 사용할 수 있는 700개 이상의 Ultra 음성을 더 추가해 보세요. 더 많은 언어를 추가할 수도 있습니다. Ultra는 36개 언어를 지원하며, language_boost를 통해 동일한 스크립트-렌더-결합 파이프라인을 모든 언어에 적용할 수 있습니다. 영구적이고 공유 가능한 오디오 URL을 위해 클라우드 스토리지 (Cloud Storage)를 추가해 보세요. 오디오북 챕터, 팟캐스트 인트로, 이러닝 (e-learning) 역할극, 게임 시네마틱 등 문맥 속에서 목소리를 듣고 싶은 모든 멀티 화자 콘텐츠를 위한 더 많은 샘플 스크립트를 추가해 보세요.
핵심은 동일합니다: 샘플 문장이 아니라, 실제 장면 속에서 Telnyx 음성들을 듣는 것입니다. 그 외의 모든 것은 여기서 파생됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기