텍스트로 SRT 자막 파일 만드는 방법: TTS로 타이밍 잡기

SRT 파일에는 줄마다 시작 시각과 끝 시각이 필요합니다. 텍스트를 TTS로 읽히면서 단어별 타이밍을 받고, 타이밍이 붙은 문장마다 번호 블록으로 쓰세요.

읽는 시간 6분Sume
전체 글

텍스트로 SRT 파일을 만들려면 텍스트의 모든 줄에 시작 시각과 끝 시각을 붙인 뒤, 번호, start --> end 타이밍 줄, 텍스트로 이루어진 번호 블록으로 쓰세요. 내레이션이라면 이 시각은 음성 자체에서 나와야 하므로, 텍스트에 먼저 오디오가 있어야 합니다. AI 음성이 읽을 텍스트라면 단어와 문장 타이밍과 함께 음성을 생성하고, 각 블록의 시각을 거기서 가져오세요. 같은 합성에서 나온 시각이므로 내레이션과 맞습니다.

Sume에서는 timestamps: { "words": true }와 segmentation: { "mode": "sentence" }를 넣은 텍스트 음성 변환(TTS)이 빈틈없는 segments[]를 반환합니다. 현재 코드에서 각 세그먼트에는 그 문장의 index, text, 초 단위의 start와 end가 담깁니다. 필드는 2026-09-28에 확인한 Sume API 레퍼런스의 TTS 스키마에서 가져왔으며, 이 레퍼런스는 API 레퍼런스 문서의 바탕이 되는 OpenAPI 문서입니다. 현재 동작이라고 설명한 결과 세부 사항은 Sume의 코드에서 확인했습니다. SRT 레이아웃 자체는 Sume 기능이 아니라 일반 텍스트로 된 자막 형식입니다.

오디오 없이 텍스트만으로 SRT 파일을 만들 수 있나요?

추측으로만 가능합니다. 텍스트만으로 SRT를 만드는 도구는 예를 들어 줄 길이를 보고 각 줄에 임의의 길이를 매겨야 하며, 음성이 그 시각에 맞춰 읽게 만드는 장치는 없습니다. 아무도 소리 내어 읽지 않는 화면 텍스트라면 그 정도로 충분할 수 있습니다. 내레이션이라면 오디오를 기준으로 텍스트의 타이밍을 잡으세요. 아래처럼 음성을 먼저 생성하거나, 녹음이 있다면 그 녹음을 전사하세요.

내 텍스트의 타이밍은 어떻게 받나요?

두 옵션을 모두 넣어 텍스트를 POST /v1/tts-1.0/generate로 보내고, Job을 폴링한 뒤, 결과에서 segments[]와 words[]를 읽으세요.

  • segmentation에는 timestamps.words: true가 필요하며, 모드는 "sentence" 하나뿐입니다.
  • 세그먼트에는 빈틈이 없습니다. 각 세그먼트는 다음 세그먼트가 시작하는 지점에서 끝납니다. 컷은 문장의 마지막 단어에서 boundary_lead_ms(0–500, 기본값 70)만큼 뒤에 놓이고, 그 뒤의 쉼은 다음 세그먼트가 흡수합니다.
  • MP3 출력이어도 괜찮습니다. mp3에서는 문장별 오디오 파일 없이 타이밍만 받으며, 문장별 오디오 파일에는 wav나 raw가 필요합니다.
  • 같은 Job이 내레이션을 만들므로, 타이밍은 바로 그 오디오 파일의 것입니다.
curl -X POST https://api.sume.com/v1/tts-1.0/generate \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: course-intro-srt-001" \
  -d '{
    "transcript": "Welcome to the course. Today we set up the project. Then we write a first test.",
    "avatar_handle": "course_host",
    "language": "en",
    "timestamps": { "words": true },
    "segmentation": { "mode": "sentence" }
  }'

세그먼트를 SRT 파일로 어떻게 바꾸나요?

세그먼트마다 블록을 하나씩 쓰세요. 시각은 세그먼트에서, 텍스트는 직접 쓴 대본에서 가져옵니다. 현재 코드에서 세그먼트의 text는 엔진의 단어 토큰으로 다시 만들어지는데, 토큰에 라틴 문자나 숫자가 있을 때만 공백으로 이어 붙이므로 한국어 텍스트는 띄어쓰기가 빠진 채 돌아옵니다.

현재 코드는 ., !, ?, 。, !, ?, …로 끝나는 단어에서 문장을 끝내며, 그 뒤에 닫는 따옴표, ), ]가 붙어 있어도 마찬가지입니다. 그러니 대본도 같은 부호에서 나누고, 두 목록의 길이가 같은지 확인하세요.

SRT의 각 부분과 TTS 결과 필드의 대응. Sume API 레퍼런스의 TTS 스키마와 Sume의 현재 코드 기준, 2026-09-28 확인.
SRT 부분가져올 곳참고
블록 번호세그먼트 index + 1index는 0부터 시작
시작 시각세그먼트 start문장 앞의 쉼을 포함. 첫 세그먼트는 0에서 시작
끝 시각세그먼트 end다음 세그먼트의 start와 같음
텍스트대본의 문장, 순서대로세그먼트 text는 단어 토큰으로 다시 만들어짐
def timecode(t):
    ms = round(t * 1000)
    h, ms = divmod(ms, 3_600_000)
    m, ms = divmod(ms, 60_000)
    s, ms = divmod(ms, 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"

def tts_to_srt(result, sentences):
    segments = result["segments"]
    if len(segments) != len(sentences):
        raise ValueError("split the script at the same end marks")
    blocks = []
    for seg, text in zip(segments, sentences):
        times = f"{timecode(seg['start'])} --> {timecode(seg['end'])}"
        blocks.append(f"{seg['index'] + 1}\n{times}\n{text}\n")
    return "\n".join(blocks)

각 줄을 말하는 동안에만 자막이 나오게 할 수 있나요?

네, 단어별 타이밍으로 할 수 있습니다. 세그먼트에는 빈틈이 없고 쉼은 다음 세그먼트가 흡수하므로, 세그먼트 타이밍을 쓴 줄은 말하기 전의 무음 동안 이미 화면에 나타나고, 첫 줄은 0에서 나타납니다. 큐를 더 촘촘하게 맞추려면 start가 세그먼트 안에 드는 단어를 words[]에서 가져와, 각 블록을 첫 단어의 start에서 시작하고 마지막 단어의 end에서 끝내세요. 같은 목록으로 긴 문장을 쉼표에서 더 짧은 큐 두 개로 나눌 수도 있습니다. 둘 다 규칙이 아니라 형식을 정하는 선택입니다.

텍스트를 이미 녹음했다면 어떻게 하나요?

그렇다면 음성을 생성하는 대신 녹음을 전사하세요. 공개 HTTPS audio_url과 같은 문장 분할 설정을 담은 음성 인식(STT) 요청 POST /v1/stt-1.0/transcribe는 index, text, start, end가 담긴 segments[]를 반환합니다. 자세한 과정은 영상에서 SRT 자막 파일을 만드는 방법에서 다룹니다. 자막을 화면에 입히려는 경우, Sume의 자막 API는 SRT 업로드를 받지 않는다는 점에 유의하세요. SRT 자막 파일을 영상에 입히는 방법에서처럼 줄을 큐로 보내세요.

비용은 얼마이고, 제한은 무엇인가요?

TTS Job은 공백과 문장 부호를 포함한 대본 글자 수에 따라 1,000자당 $0.0475로 과금되며, 기본적으로 5.5% 에이전트 수수료가 더해집니다. 현재 코드에서 예상 금액은 글자 수만 세므로 타이밍을 요청해도 비용이 늘지 않습니다. 요청 하나는 최대 20,000자를 받으며, 오디오가 1,200초를 넘으면 tts_duration_exceeded로 실패하고 크레딧은 확정되지 않습니다. 더 긴 텍스트는 나눠서 합성하세요. 부분들을 이어 붙일 때는 각 부분의 시각을 그 앞에 있는 부분들의 길이를 모두 더한 만큼 뒤로 옮기세요.

출처

관련 글

개발자 카테고리의 다른 글

개발자 글 전체 보기

작성자 Sume