포스트

숏폼 나레이션이 배속 재생처럼 빨라질 때 — 컷 길이를 말에 맞추기

컷 길이를 눈대중으로 정하면 나레이션이 컷을 넘쳐 말속도를 올려 욱여넣게 된다. 문장을 실제로 합성해 재서 컷 길이를 역산하는 방법과, 컷별로는 멀쩡한데 이어 읽으면 이상해지는 대본을 잡아내는 점검표.

숏폼 나레이션이 배속 재생처럼 빨라질 때 — 컷 길이를 말에 맞추기

🗺 시리즈: 네이버 클립 자동 제작 — 지금은 ④ 컷 길이를 말에 맞추기

← 이전 편: ③ 레퍼런스에서 내 나레이션 목소리 찾기

① 컷시트 렌더 엔진 · ② 한 글자씩 찍히는 타이핑 자막 · ③ 레퍼런스에서 내 나레이션 목소리 찾기 · 컷 길이를 말에 맞추기 (현재 글)

지난 편에서 나레이션 목소리를 정했다. 그런데 붙여놓고 들어보니 말이 급했다.

NAVER 클립 0:45 완성본 들어보기 ↗

22문장 중 17개가 속도 조절 없이 그대로 나가는 최종본

문제

나레이션은 컷 하나에 문장 하나씩 배치된다. 문장이 컷보다 길면 컷을 넘치니, 넘치는 만큼 말속도를 올려서 맞췄다.

1
2
3
4
5
6
7
001 |  1.80s →  2.02s | r=28 | 비 오면 여기로 가세요
002 |  4.80s →  1.82s | r=34 | 토요일에 비가 왔고
…
015 | 35.30s →  1.82s | r=55 | 1층 카트가 내려다보여요
018 | 42.20s →  2.14s | r=55 | 그 코인은 게임존 뽑기에 쓰고
019 | 44.40s →  2.02s | r=55 | 소꿉놀이방에서 안 나오더라고요
020 | 46.40s →  1.78s | r=55 | 비 와서 망한 줄 알았는데

r이 속도 증가율이다. 상한으로 걸어둔 +55%에 붙은 문장이 다섯 개. 1.55배는 사람 말이 아니라 배속 재생으로 들린다. 특히 마무리 문장이 급하면 영상 전체가 허둥댄 느낌으로 끝난다.

원인

컷 길이를 내가 눈대중으로 정했기 때문이다.

컷시트를 쓸 때는 “이 정도면 2초쯤” 하고 적는다. 그림만 볼 때는 맞는 감이다. 그런데 거기에 12음절짜리 문장을 얹으면 이야기가 달라진다. 한국어 뉴럴 음성으로 소꿉놀이방에서 안 나오더라고요를 읽으면 자연스러운 속도로 2.9초가 걸린다. 2초짜리 컷에 넣으려면 1.45배로 몰아야 한다.

문제는 음성이 아니라 컷이었다. 그런데 나는 음성 쪽 손잡이(속도)만 돌리고 있었다.

해결 과정

처음 시도 — 컷 경계를 조금 넘게 해줬다 (틀렸다)

“컷을 0.35초까지는 넘어가도 되게 하자”고 생각했다. 실제 영상에서도 나레이션이 컷을 살짝 타고 넘는다.

1
LIMIT=$(awk "BEGIN{printf \"%.2f\", $DUR+0.35-0.12}")

숫자상으로는 좋아졌다. r=55가 몇 개 사라졌다.

그런데 이건 틀린 완화였다. 컷마다 문장이 하나씩 있으니, 이 컷을 0.35초 넘긴다는 건 다음 문장이 시작한 뒤에도 앞 문장이 말하고 있다는 뜻이다. 두 목소리가 겹친다. 화면 자막은 이미 다음 문장인데 소리는 앞 문장을 마저 읽는다.

허용 시간을 “다음 문장이 시작할 때까지”로

경계를 컷이 아니라 다음 문장의 시작점으로 잡았다. 이러면 겹칠 일이 없고, 자막 없는 컷이 뒤따르거나 마지막 문장이면 그만큼 여유가 저절로 생긴다.

1
2
3
4
5
6
awk -F'\t' -v total="$T" '{st[NR]=$1; du[NR]=$2; tx[NR]=$3}
  END{for(i=1;i<=NR;i++){
        nx = (i<NR) ? st[i+1] : total;      # 다음 문장 시작, 없으면 영상 끝
        w  = nx - st[i];
        m  = du[i] + 0.9; if (w > m) w = m;  # 자막과 너무 어긋나지 않게 상한
        printf "%s\t%.2f\t%s\n", st[i], w, tx[i]}}' lines.txt > win.txt

컷 길이 + 0.9초 상한을 둔 이유는, 여유가 아무리 많아도 자막이 바뀐 뒤까지 한 문장을 끌면 눈과 귀가 따로 놀기 때문이다.

이걸로 마지막 문장(여긴 추천합니다)은 r=55에서 r=0으로 떨어졌다. 뒤에 자막 없는 컷이 있어 여유가 생긴 것이다. 하지만 중간의 빡빡한 문장들은 그대로였다.

근본 해결 — 컷 길이를 문장에서 역산한다

방향을 뒤집었다. 컷 길이를 정하고 문장을 맞추는 게 아니라, 문장을 재고 컷 길이를 정한다.

문장 하나하나를 실제로 합성해 앞뒤 무음을 잘라낸 길이를 잰다. 그 값에 숨 쉴 여백을 더한 게 그 컷의 최소 길이다.

1
2
3
4
5
6
7
8
def spoken(text):
    """문장을 실제로 합성해 앞뒤 무음을 뺀 길이를 잰다."""
    subprocess.run([EDGE, '--voice', VOICE, '--rate', '+35%', '--text', text,
                    '--write-media', mp3], check=True, capture_output=True)
    subprocess.run(['ffmpeg','-y','-v','error','-i',mp3,'-af',TRIM,wav], check=True)
    return probe(wav)

need = round(max(FLOOR, spoken(main) + MARGIN), 1)   # FLOOR=2.0, MARGIN=0.35

두 값을 정했다.

  • 최소 2.0초 — 이보다 짧으면 자막을 읽을 시간이 없다. 타이핑 자막이면 더욱 그렇다
  • 여백 0.35초 — 문장 끝과 컷 끝이 딱 붙으면 숨 쉴 틈 없이 다음 컷으로 넘어간다

원본 영상이 모자랄 때

컷을 늘리려면 원본 영상에 그만큼 남아 있어야 한다. 4.5초짜리 영상의 3.0초 지점부터 2.9초를 쓸 수는 없다.

먼저 시작점을 앞으로 당겨보고, 그래도 모자라면 컷을 줄이되 경고를 남긴다.

1
2
3
4
5
6
7
8
st = float(start); avail = src_dur(src) - st - 0.08
if need > avail:
    shift = min(st - 0.2, need - avail)
    if shift > 0:
        st = round(st - shift, 1); avail = src_dur(src) - st - 0.08
    if need > avail:
        log.append(f"  ! {src} 원본이 짧다 → {need:.1f} 대신 {avail:.1f}초  [{main}]")
        need = round(avail, 1)

경고를 조용히 삼키지 않는 게 중요하다. 그 컷만 말이 빨라지므로, 사람이 보고 “다른 소재로 바꿀지” 판단해야 한다. 실제로 이 경고가 뜬 컷 하나는 소재를 바꿀 수 없어서(그 장면이 그 영상에만 있었다) 감수하고 넘어갔다.

결과

 고치기 전고친 뒤
속도 조절 없이 나가는 문장3개17개
상한(+55%)에 붙은 문장5개0개
최대 속도 증가율+55%+19%
영상 길이51.7초45.9초

영상이 오히려 짧아졌다. 컷을 문장에 맞추니 불필요하게 길게 잡아뒀던 컷들이 줄었기 때문이다. 말은 느려졌는데 영상은 짧아진 셈이다.

문장을 합쳐 한 트랙으로 만들 때 뒤로 갈수록 소리가 커지는 문제가 따로 있다 → ffmpeg amix로 합쳤더니 뒤로 갈수록 소리가 커진다

대본은 컷별로 보면 안 된다

여기까지가 시간 문제였다. 그런데 다 맞춰놓고 처음부터 들어보니 다른 종류의 문제가 드러났다.

컷시트는 한 줄씩 쓴다. 그래서 줄 단위로는 다 멀쩡한데 이어 읽으면 이상한 대본이 나온다. 다섯 가지가 걸렸다.

증상실제로 있었던 것
소리 구멍요금표 컷에 상단 배지만 넣고 본자막을 비웠다. 나레이션은 본자막에서 읽으므로 2.4초 동안 소리가 통째로 끊겼다
동선 왕복안쪽엔 카트 트랙테마존 옆이 게임존2층. 안쪽까지 갔다가 도로 나와서 다시 올라간다
끊긴 전환그 코인은 게임존 뽑기에 쓰고소꿉놀이방에서 안 나오더라고요. 연결이 없어 뜬금없다
같은 단어 반복무료가 세 번(게임기는 전부 무료래요 / (이것도 무료) / (여기도 전부 무료))
직전 컷과 중복2층으로 올라가면 바로 뒤에 2층 사냥터에서…

고친 방식은 이렇다.

  • 소리 구멍 — 안내판이 화면을 꽉 채워 자막을 못 넣던 상황이었다. 안내판을 위쪽에 배치한 그림을 새로 만들어 아래를 비우고 자막을 넣었다. 배지는 나레이션으로 읽히지 않는다는 걸 몰랐던 게 원인이다
  • 동선 왕복 — 공간 순서대로 재배열
  • 끊긴 전환정작 제일 오래 논 덴 소꿉놀이방으로 이어 붙였다
  • 반복·중복 — 정보값이 낮은 쪽을 뺐다

이걸 잡으려고 대본을 시간순으로 출력해 소리 내어 이어 읽는 단계를 절차에 넣었다.

1
2
3
4
5
  1.8초  비 오면 여기로 가세요
  3.8초  토요일에 비가 왔고
  5.8초  갈 데 찾다 들어온 월드킹
 …
 44.2초  여긴 추천합니다

22개 컷의 중앙 프레임을 격자로 붙인 검수시트 그림 쪽 점검은 1편의 검수시트가, 말 쪽 점검은 위의 시간순 대본이 맡는다.

🧭 기획자·사업자라면 분량 산정을 감으로 하면 반드시 뒤에서 터진다. “이 정도면 2초쯤”이 틀린 게 아니라, 그 감이 그림 기준이었고 거기에 말이 얹히리라는 걸 계산에 안 넣었을 뿐이다. 제약이 둘 이상 얹히는 항목은 눈대중이 아니라 실측으로 역산해야 한다. 그리고 줄 단위 검수는 흐름을 못 잡는다. 항목별로는 다 통과하는데 이어 놓으면 이상한 산출물 — 페이지 단위로 리뷰한 사용자 흐름, 조항별로 검토한 계약서, 슬라이드별로 본 제안서가 다 같은 함정이다. 완성물을 처음부터 끝까지 한 번 통과시키는 단계를 절차에 따로 박아둬야 한다.

사용한 기술

  • edge-tts + silenceremove — 문장별 실제 발화 길이 측정. 앞뒤 무음을 빼야 정확하다
  • awk 2패스 — 문장 목록을 훑어 “다음 문장 시작까지”의 창을 계산
  • ffprobe — 원본 영상 길이를 재서 컷을 늘릴 여유가 있는지 확인
  • 컷시트 제자리 수정 — 역산 결과를 컷시트에 바로 반영하고, 못 맞춘 컷은 경고로 남긴다

정리

  • 말이 빠른 건 음성 설정 문제가 아니라 컷이 짧아서다. 속도 손잡이를 돌리기 전에 컷 길이를 문장에서 역산하라.
  • “조금 넘쳐도 된다”는 완화는 위험하다. 컷당 문장이 하나면 그 넘침은 다음 문장과의 겹침이다. 경계는 컷이 아니라 다음 문장의 시작점으로 잡아야 한다.
  • 컷별로 멀쩡한 대본이 이어 읽으면 이상할 수 있다. 소리 구멍·동선 왕복·끊긴 전환은 시간순으로 통째로 읽어야만 보인다.

시리즈는 여기까지다. 컷시트 한 장에서 시작해 자막이 찍히고 목소리가 얹히고 길이가 맞춰진 결과물이 이 클립이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.