인터랙티브 음악 애플리케이션, 게임 NPC의 실시간 가창 기능, 버추얼 스트리머(VTuber) 오디오 합성 파이프라인을 구축하는 엔지니어들은 종종 웹 브라우저 기반 UI의 한계에 부딪힙니다. 브라우저에서의 수동 클릭 작업은 자동화된 대규모 배치 작업이나 실시간 프로덕션 파이프라인을 감당할 수 없습니다. Uberduck은 개발자가 코드에서 직접 리듬감과 선율을 갖춘 보컬 음원을 프로그래밍 방식으로 생성할 수 있도록 공식 REST API를 제공합니다.
Uberduck API를 성공적으로 연동하여 랩과 노래 보컬을 자동 생성하려면 HTTP Basic 인증, 합성 모드 선택(비트 동기화 기반 /tts/freestyle, 피치 타임라인 제어 기반 /speak, 음성 변환 기반 /tts/convert), 그리고 /speak-status 비동기 작업 폴링 루프라는 세 가지 핵심 계층을 구축해야 합니다.
1. 인증 메커니즘 및 요청 아키텍처
Uberduck REST API는 표준 HTTP Basic Authentication 프로토콜을 사용합니다. 대시보드에서 발급받은 API Key(사용자 이름)와 API Secret(비밀번호)을 콜론(:)으로 연결한 뒤 Base64로 인코딩하여 요청 헤더에 포함해야 합니다.
인증 헤더 구성
- Uberduck 계정 설정 대시보드에서
API Key및API Secret을 생성합니다. - HTTP
Authorization헤더를 다음과 같이 작성합니다:Authorization: Basic base64(API_KEY:API_SECRET) - API 기본 호출 주소는
https://api.uberduck.ai입니다.
import base64
import os
API_KEY = os.getenv("UBERDUCK_API_KEY", "your_api_key")
API_SECRET = os.getenv("UBERDUCK_API_SECRET", "your_api_secret")
def get_auth_headers():
token = base64.b64encode(f"{API_KEY}:{API_SECRET}".encode("utf-8")).decode("utf-8")
return {
"Authorization": f"Basic {token}",
"Content-Type": "application/json"
}
2. 세 가지 보컬 합성 방식 비교
애플리케이션이 박자에 맞춘 리드미컬한 랩을 필요로 하는지, 아니면 멜로디가 중심이 되는 가창을 요구하는지에 따라 Uberduck은 세 가지 경로를 제공합니다:
| 합성 방식 | 핵심 엔드포인트 | 주요 파라미터 | 권장 사용 시나리오 |
|---|---|---|---|
| 텍스트 기반 랩 (Freestyle) | POST /tts/freestyle | lyrics, bpm, backing_track, voice 또는 voicemodel_uuid | 비트에 맞춘 랩 트랙 생성; 대화형 프리스타일 랩 생성기; 박자 동기화 라임 연출 |
| 파라미터 기반 피치 및 음소 길이 제어 | POST /speak | speech, voicemodel_uuid, pitch, duration, pace | MIDI 또는 악보 데이터 기반으로 음소별 음높이 곡선과 지속 시간을 정밀 제어하는 곡 합성 |
| 음성 변환 (Voice-to-Voice) | POST /tts/convert | voicemodel_uuid, input (오디오 URL), pitch_shift | 자연스러운 비브라토와 슬라이드가 반영된 선율 가창; 가이드 보컬 기반 캐릭터 음색 변환 |
3. 엔드투엔드 자동 랩 합성 파이프라인 구축
/tts/freestyle 엔드포인트는 랩 전용으로 설계되어 지정된 템포(BPM) 및 반주 트랙 구조에 맞춰 가사의 음절을 자동으로 정렬합니다.
1단계: 사용 가능한 반주(Backing Track) 조회
Uberduck이 제공하는 기본 비트 트랙과 가사를 동기화하려면 반주 목록을 조회합니다:
import requests
def list_backing_tracks():
url = "https://api.uberduck.ai/reference-audio/backing-tracks?detailed=true"
response = requests.get(url, headers=get_auth_headers())
response.raise_for_status()
return response.json() # 트랙 UUID, BPM, 장르 메타데이터 목록 반환
2단계: (선택 사항) 라임 가사 자동 생성
사용자가 입력한 주제에 따라 즉흥적으로 라임을 생성해야 하는 경우 /tts/lyrics를 호출하거나 LLM 출력을 2차원 문자열 배열 형태로 구성합니다:
def generate_lyrics(subject, lines=4):
url = "https://api.uberduck.ai/tts/lyrics"
payload = {
"subject": subject,
"lines": lines
}
response = requests.post(url, json=payload, headers=get_auth_headers())
response.raise_for_status()
return response.json() # 구조화된 가사 행 반환
3단계: Freestyle 생성 요청 전달
구성된 가사 매트릭스, 목표 BPM, 반주 트랙 UUID, 대상 보이스 모델 파라미터를 엔드포인트로 전송합니다:
def generate_rap_vocal(lyrics_matrix, bpm=90, backing_track_uuid=None, voice="zwf"):
url = "https://api.uberduck.ai/tts/freestyle"
payload = {
"lyrics": lyrics_matrix, # 예시: [["서버에 흐르는 코드 비트에 맞춰 뛰고", "API 호출 소리 트랙 위를 덮고"]]
"bpm": bpm,
"voice": voice,
"format": "json"
}
if backing_track_uuid:
payload["backing_track"] = backing_track_uuid
response = requests.post(url, json=payload, headers=get_auth_headers())
response.raise_for_status()
data = response.json()
return data["uuid"] # 비동기 작업 UUID 반환
4. 음높이(Pitch) 타임라인과 노래 보컬 제어
멜로디가 있는 가창 보컬은 음높이의 연속적인 변화와 음소 지속 시간에 대한 세밀한 제어가 필요합니다.
접근법 A: /speak를 통한 피치 배열 및 지속 시간 제어
세부 제어를 지원하는 보이스 모델(GET /voices/{voicemodel_uuid}/detail 응답에서 controls: true 확인)의 경우, /speak 호출 시 pitch 배열과 duration 배열을 직접 전달할 수 있습니다:
def generate_singing_notes(speech_text, voicemodel_uuid, pitch_curve, duration_curve):
"""
pitch_curve: 각 음소에 대응하는 목표 피치 수치(반음 또는 F0 주파수) 배열
duration_curve: 각 음소의 지속 시간을 나타내는 수치 배열
"""
url = "https://api.uberduck.ai/speak"
payload = {
"speech": speech_text,
"voicemodel_uuid": voicemodel_uuid,
"pitch": pitch_curve,
"duration": duration_curve,
"pace": 1.0
}
response = requests.post(url, json=payload, headers=get_auth_headers())
response.raise_for_status()
return response.json()["uuid"]
접근법 B: 음성 변환(V2V)을 통한 자연스러운 가창 구현
텍스트 상태에서 음소별 피치 수치를 일일이 계산하는 작업은 수식 복잡도가 높고 자칫 기계음처럼 들릴 수 있습니다. 상용 프로덕션에서는 Voice-to-Voice(음성 변환) 방식이 널리 채택됩니다:
MIDI 보컬 신시사이저나 가이드 보컬 녹음 음원을 생성한 뒤 POST /tts/convert로 변환하고, pitch_shift로 반음 조정을 적용하는 방식입니다:
def convert_guide_vocal_to_singer(guide_audio_url, target_voice_uuid, semitone_shift=0):
url = "https://api.uberduck.ai/tts/convert"
payload = {
"input": guide_audio_url,
"voicemodel_uuid": target_voice_uuid,
"pitch_shift": semitone_shift
}
response = requests.post(url, json=payload, headers=get_auth_headers())
response.raise_for_status()
return response.json()["uuid"]
5. 비동기 작업 수명 주기 및 폴링 파이프라인
오디오 신경망 렌더링은 연산량이 커서 단일 HTTP 요청 안에서 즉시 완료되지 않습니다. 모든 합성 요청은 비동기 작업 식별자(uuid)를 반환합니다. 백엔드 시스템은 /speak-status 엔드포인트를 주기적으로 확인해야 합니다.
import time
def poll_render_completion(job_uuid, max_attempts=30, delay_seconds=2):
status_url = f"https://api.uberduck.ai/speak-status?uuid={job_uuid}"
headers = get_auth_headers()
for attempt in range(max_attempts):
response = requests.get(status_url, headers=headers)
response.raise_for_status()
data = response.json()
# 실패 처리 확인
if data.get("failed_at"):
raise RuntimeError(f"보컬 합성 작업 실패: {data}")
# 완료 여부 확인
if data.get("finished_at"):
audio_url = data.get("path")
return audio_url # 렌더링된 CDN 파일 경로
time.sleep(delay_seconds)
raise TimeoutError("합성 작업 대기 시간이 초과되었습니다.")
6. 엔드투엔드 파이썬 연동 스크립트
인증, 요청 전송, 상태 폴링, 음원 다운로드까지 전체 흐름을 수행하는 파이썬 스크립트 예제입니다:
import requests
import base64
import time
import os
UBERDUCK_API_KEY = os.getenv("UBERDUCK_API_KEY")
UBERDUCK_API_SECRET = os.getenv("UBERDUCK_API_SECRET")
HEADERS = {
"Authorization": f"Basic {base64.b64encode(f'{UBERDUCK_API_KEY}:{UBERDUCK_API_SECRET}'.encode()).decode()}",
"Content-Type": "application/json"
}
def create_rap_track(lyrics, bpm=95, voice="zwf", output_file="vocal_output.wav"):
# 1. 랩 생성 작업 등록
print("보컬 합성 요청을 전송하는 중...")
freestyle_url = "https://api.uberduck.ai/tts/freestyle"
payload = {
"lyrics": lyrics,
"bpm": bpm,
"voice": voice,
"format": "json"
}
resp = requests.post(freestyle_url, json=payload, headers=HEADERS)
resp.raise_for_status()
task_id = resp.json()["uuid"]
print(f"작업 생성 완료, UUID: {task_id}")
# 2. 상태 폴링
status_url = f"https://api.uberduck.ai/speak-status?uuid={task_id}"
audio_url = None
for _ in range(40):
time.sleep(2)
status_resp = requests.get(status_url, headers=HEADERS)
status_resp.raise_for_status()
status_data = status_resp.json()
if status_data.get("failed_at"):
raise RuntimeError("서버에서 렌더링 중 오류가 발생했습니다.")
if status_data.get("finished_at"):
audio_url = status_data.get("path")
print(f"합성 완료! 다운로드 URL: {audio_url}")
break
if not audio_url:
raise TimeoutError("80초 이내에 렌더링이 완료되지 않았습니다.")
# 3. 오디오 다운로드 및 저장
audio_data = requests.get(audio_url).content
with open(output_file, "wb") as f:
f.write(audio_data)
print(f"오디오 파일 저장 완료: {output_file}")
return output_file
if __name__ == "__main__":
bars = [
["자동화 파이프라인 리듬을 타고 흐르고", "데이터가 쏟아져도 비트는 계속 달리고"],
["템포에 맞춰 떨어지는 완벽한 BPM", "Uberduck API로 완성하는 새로운 음원"]
]
create_rap_track(lyrics=bars, bpm=95)
7. 프로덕션 운영 권장사항 및 트레이드오프
실제 상용 서비스에 Uberduck API를 연동할 때는 다음 사항을 고려해야 합니다:
- 음절 밀도와 BPM의 균형: 한 줄당 가사 음절 수가 설정된 템포와 맞지 않으면 발음이 뭉개지거나 음성 끊김 현상이 발생할 수 있습니다. 110 BPM 이상의 빠른 템포에서는 지나치게 긴 가사를 피해야 합니다.
- 비동기 큐 분리: 작업 완료까지 대략 4~15초가 소요됩니다. 웹 요청의 메인 스레드를 직접 블로킹하지 말고 Celery, BullMQ 등의 백그라운드 큐를 활용해 비동기로 처리하십시오.
- 상용 라이선스 및 유료 플랜: 상용 애플리케이션에서 API를 호출하려면 Pro 또는 Enterprise 등 공식 상용 권한이 포함된 유료 플랜을 구독해야 합니다. 무료 플랜은 비상용 용도로 제한됩니다.
- 오디오 후처리(DSP): API에서 다운로드한 오디오는 마스터링 이전의 드라이(Dry) 상태입니다. 완성도 높은 음원을 위해 이퀄라이저, 컴프레서, 리버브 등 후처리 이펙터를 파이프라인 후단에 배치하는 것을 권장합니다.
참고 자료
- Uberduck API Documentation
- Generate Freestyle - Uberduck Text To Speech API
- Generate Lyrics - Uberduck Text To Speech API
- Generate Speech - Uberduck Text To Speech API
- Voice To Voice Conversion - Uberduck Text To Speech API
- Get text-to-speech request status - Uberduck Text To Speech API
- List Backing Tracks - Uberduck Text To Speech API
- Uberduck AI Pricing and Plans