Skip to main content
/audio/speech를 한 번 호출하는 것은 쉽습니다. 실제 기사를 낭독하는 순간부터 흥미로운 문제들이 드러납니다. 엔드포인트는 요청당 최대 4096자를 받고, 모든 음성은 특정 모델에 속하며, 오디오 형식은 모델마다 다르고, 읽기 위해 쓴 텍스트는 듣기 위해 쓴 텍스트와 전혀 다르게 생겼습니다. 이 튜토리얼에서 그 네 가지를 모두 다룹니다. 결과물은 URL을 하나의 오디오 파일로 바꾸는 스크립트입니다:
우리는 다음을 수행합니다:
  1. 긴 형식의 낭독에 적합한 모델과 음성을 고릅니다
  2. 하나의 음성 요청을 보내고 오디오를 저장합니다
  3. 긴 기사를 문자 제한에 맞도록 청크(chunk)로 분할합니다
  4. 합성된 청크를 이음새가 들리지 않도록 하나의 파일로 결합합니다
  5. 기사를 들을 만한 형태로 다시 씁니다
  6. 조각들을 결합하고, 이어서 대화형 사용을 위한 스트리밍을 살펴봅니다

준비 사항

Python 3.9 이상, requests 패키지, 그리고 Venice API 키가 필요합니다.

1. 모델과 음성 선택하기

음성은 모델에 속합니다. 한 모델 계열의 음성을 다른 계열의 모델에 보내는 것이 가장 흔한 첫 실수이므로, 각 모델이 실제로 무엇을 받아들이는지 나열하는 것으로 시작하세요:
model_spec.voices는 해당 모델의 공식적인 음성 목록이며, supported_formats는 어떤 response_format 값들을 받아들이는지 알려줍니다. 쿼리에서 | length를 빼면 음성 이름 자체를 출력할 수 있습니다. 우리는 음성 eve와 함께 tts-xai-v1을 사용합니다. 이 모델은 pcm을 지원하는데, 이는 4단계에서 청크를 결합하는 작업을 단순하게 만들어 줍니다.
합성 속도는 TTS 모델 사이에서 출력 품질보다 훨씬 더 크게 차이 나며, 그 격차는 아키텍처를 바꿀 만큼 큽니다. 확정하기 전에 두세 개의 후보를 대상으로 현실적인 요청의 시간을 측정해 보세요. 한 모델에서 몇 초 만에 돌아오는 청크가 다른 모델에서는 몇 분이 걸릴 수 있습니다.

2. 하나의 요청 보내기

응답 본문은 JSON이 아니라 원시(raw) 오디오이므로, 바이트를 그대로 파일에 기록하세요.
일치하지 않는 조합은 오디오가 생성되기 전에 거부되며, 에러 메시지는 대신 어떤 값을 사용하면 되는지 알려줍니다:

3. 4096자 제한에 맞춰 텍스트 분할하기

input 필드는 최대 4096자를 받습니다. 더 긴 텍스트는 조용히 잘리지 않고 아예 거부됩니다:
그래서 기사를 먼저 분할합니다. 문장 경계에서 분할하는 것이 중요합니다. 문장 중간에서 끝나는 청크는 결합부에서 눈에 띄는 어긋남을 만들기 때문입니다. narrate.py를 생성하세요:
5362자짜리 스크립트에서 이 함수는 각각 문장으로 끝나는 네 개의 청크를 만듭니다:
기본 max_chars가 4096 상한 근처가 아니라 1500인 것은 의도적입니다. 합성 시간은 입력 길이에 따라 늘어나므로, 작은 청크가 더 빨리 돌아오고, 병렬로 실행되기 때문에 전체 작업을 더 빨리 끝냅니다. 또한 한 요청이 실패했을 때 재시도 비용도 저렴해집니다.

4. 청크를 하나의 파일로 결합하기

MP3와 같은 인코딩된 오디오를 이어붙이는 것은 신뢰할 수 없습니다. 각 청크가 자체 프레임 헤더를 지니고 있기 때문입니다. pcm을 요청하면 이 문제를 완전히 피할 수 있습니다. PCM은 컨테이너가 없는 원시 샘플이므로 결합은 단순히 바이트를 이어붙이는 것이고, Python 표준 라이브러리의 wave 모듈이 헤더를 대신 써 줍니다.
단일 청크는 자신이 포함한 오디오 양에 비해 빠르게 돌아옵니다:
이 요청은 89초 분량의 음성을 생성하는 데 약 13초가 걸렸습니다. 네 청크를 동시에 실행하는 것이 전체 시간을 합리적으로 유지하는 비결입니다. 아래의 전체 낭독은 실제로 15초의 벽시계 시간이 걸렸습니다. ThreadPoolExecutor.map은 입력이 제출된 순서로 결과를 반환하므로, 같은 시간에 합성되었더라도 청크는 읽기 순서대로 정렬됩니다.
원시 PCM은 샘플 레이트를 포함하지 않으므로 WAV 헤더를 쓸 때 올바른 값을 직접 제공해야 하며, 이 값은 모델별로 다릅니다. tts-xai-v1은 24 kHz를, tts-gradium-v1은 48 kHz를 반환합니다. 잘못 추측하면 낭독이 잘못된 속도와 음정으로 재생됩니다.
임의의 모델에 대한 샘플 레이트를 찾으려면, 짧은 클립을 wav로 요청한 뒤 돌아온 헤더를 읽으세요:

5. 자연스럽게 들리는 텍스트 준비하기

스크랩된 Markdown을 그대로 낭독하면 거의 들을 수 없는 수준이 됩니다. URL이 가장 명확한 예입니다. 음성 모델은 URL을 한 글자씩 읽으므로 https://docs.venice.ai/llms.txt가 이렇게 나옵니다:
h t t p s colon slash slash docs dot venice dot a i l l m s dot t x t
제목, 불릿, 표, 코드 블록도 같은 문제를 더 작은 규모로 일으킵니다. 정규표현식으로 Markdown과 싸우는 대신, 채팅 모델에게 기사를 말로 하기에 적합한 형태로 다시 쓰도록 요청할 수 있습니다. article_to_audio.py를 만드세요:
URL_PATTERN 치환은 모델이 남긴 링크에 대한 안전망으로 남아 있습니다.

6. 하나로 합치기

진입점은 스크래핑하고, 스크립트를 작성하고, 저장한 뒤 낭독합니다:
script.txt를 오디오 옆에 저장하는 두 줄은 충분히 가치가 있습니다. 낭독이 이상하게 들릴 때 스크립트가 거의 항상 이유를 보여주며, 다시 비용을 들여 합성하지 않고도 수정할 수 있기 때문입니다.
6분에 조금 못 미치는 낭독이 약 15초 만에 생성되었습니다. 스크립트는 이제 내비게이션 요소가 아니라 산문으로 시작합니다:
Venice is built on a simple but powerful principle. User privacy comes first. The platform’s entire architecture flows from this philosophical commitment.
낭독 전체를 앉아서 듣지 않고 확인하려면, 오디오를 /audio/transcriptions로 다시 보내 그 결과를 script.txt와 비교하세요. 마지막 20초를 전사(transcribe)하는 것만으로도 청크가 올바른 순서로 결합되었는지 빠르게 확인할 수 있으며, 누락된 청크나 한 글자씩 읽힌 URL을 몇 초 만에 잡아냅니다.

대화형 사용을 위한 스트리밍

배치 낭독은 전체 시간을 최적화합니다. 음성 인터페이스는 그와 정반대의 우선순위, 즉 첫 오디오를 최대한 빨리 내보내는 것을 우선합니다. streaming: true를 설정하면 본문이 생성되는 대로 문장 단위로 반환되어, 클립 전체를 기다리는 대신 약 1초 만에 재생을 시작할 수 있습니다.
브라우저의 Web Audio API나 오디오 장치에 직접 전달할 때는 디코딩 단계가 필요 없으므로 mp3보다 pcm을 선호하세요.

알아둘 만한 요청 옵션

에러

청크가 서로 독립적이기 때문에, 실패는 언제나 한 청크만 대가로 치르게 되며, 그 청크에 대해 synthesize를 재시도하는 것은 언제나 안전합니다.

다음 단계

여기서 자연스럽게 이어갈 수 있는 확장 몇 가지:
  • 텍스트, 음성, 모델의 해시를 키로 오디오를 캐시해서, 변경되지 않은 단락은 결코 다시 합성하지 않게 하세요.
  • 음성 클로닝으로 여러분 자신의 음성을 낭독에 사용하도록 클로닝된 음성으로 교체하세요.
  • 스크랩하는 대신, 웹 검색으로 인용 답변 만들기를 사용해 소스 텍스트 자체를 생성하세요.
  • 음악과 사운드 이펙트로 인트로나 배경 오디오를 추가하세요.

텍스트-음성 변환

speech 엔드포인트와 매개변수에 대한 레퍼런스.

음성 클로닝

프리셋 대신 사용자 지정 음성으로 낭독하세요.

웹 검색으로 인용 답변 만들기

이 도구가 낭독할 텍스트를 생성하세요.

음성-텍스트 변환

낭독을 종단 간(end to end)으로 검증하기 위해 오디오를 전사하세요.