광고 자리 모집 중 · 제휴 문의

Uberduck API를 연동하여 AI 랩과 노래 보컬을 프로그래밍 방식으로 생성하는 방법

365Finds Editorial
Wiki 문서 11,430편 수록

내 AI에서 365 Wiki 사용하기

아래 프롬프트를 복사해 평소 사용하는 AI에 붙여넣고 바로 질문하세요.

전체 Skill 다운로드

먼저 프롬프트로 체험해 보세요. 365 Wiki를 자주 쓴다면 전체 365Wiki Skill을 설치해 매번 붙여넣는 수고를 줄일 수 있습니다. 평소 쓰는 AI가 없다면 Gemini나 Microsoft Copilot 웹 버전도 사용할 수 있습니다.

인터랙티브 음악 애플리케이션, 게임 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로 인코딩하여 요청 헤더에 포함해야 합니다.

인증 헤더 구성

  1. Uberduck 계정 설정 대시보드에서 API Key 및 API Secret을 생성합니다.
  2. HTTP Authorization 헤더를 다음과 같이 작성합니다:
    Authorization: Basic base64(API_KEY:API_SECRET)
    
  3. 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/freestylelyrics, bpm, backing_track, voice 또는 voicemodel_uuid비트에 맞춘 랩 트랙 생성; 대화형 프리스타일 랩 생성기; 박자 동기화 라임 연출
파라미터 기반 피치 및 음소 길이 제어POST /speakspeech, voicemodel_uuid, pitch, duration, paceMIDI 또는 악보 데이터 기반으로 음소별 음높이 곡선과 지속 시간을 정밀 제어하는 곡 합성
음성 변환 (Voice-to-Voice)POST /tts/convertvoicemodel_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를 연동할 때는 다음 사항을 고려해야 합니다:

참고 자료