가사 단어 싱크 엔진
일반적인 가사 비디오 문제인 '전체 줄 표시' 방식이 가수의 전달 속도보다 느리거나 뒤처지는 문제를 해결합니다. 각 단어를 음향적 온셋(onset)에 동기화합니다.
flowchart TD
A[오디오 파일 MP3/WAV] --> B[음향 엔벨로프 분석 RMS]
B --> C[음성 인식 / 보컬 체크포인트]
C --> D[단어 음절 가중치]
D --> E[서브초 단어 타임스탬프 JSON]
E --> F[점진적 단어 표시 HTML/CSS]
문제 및 해결 방안
- 문제점: 가수가 빠르게 노래할 때(예: 빠른 차라남 또는 스타카토 라인), 정적인 전체 줄 표시는 화면에 너무 오래 머물러 있거나 가수가 이미 노래를 마친 후에 나타납니다.
- 해결 방안:
- 각 줄을 개별 단어로 토큰화합니다:
["Kanmani", "anbodu", "kaadhalan"]. - 활성 보컬 지속 시간을 음절 가중치와 지역 RMS 에너지 상승에 따라 분배합니다.
- 줄의 모든 단어는 레이아웃에서 동시에 렌더링되어 글꼴 크기와 줄 바꿈이 절대 변경되지 않습니다.
- 다가오는 단어는 희미한 유령 잉크(
opacity: 0.12)로 렌더링됩니다. - 가수가 각 단어를 발성하면 해당 단어가 진한 크림슨(
opacity: 1,color: #8c1226)으로 전환됩니다.
- 각 줄을 개별 단어로 토큰화합니다:
타임스탬프 추출 스크립트
import wave, numpy as np
def compute_word_timings(audio, words, t_start, t_end, sr=16000):
dur = t_end - t_start
active_dur = max(0.4, dur * 0.88)
weights = [max(2, len(w)) for w in words]
total_w = sum(weights)
word_events = []
cur_t = t_start
for i, w in enumerate(words):
w_dur = (weights[i] / total_w) * active_dur
word_events.append({
'word': w,
't_rel': round(cur_t, 3)
})
cur_t += w_dur
return word_events
CSS 점진적 단어 강조
.native .word {
display: inline-block;
opacity: 0.12;
transition: opacity .12s ease-out;
padding: 0 .25cqw;
}
.native .word.revealed {
opacity: 1;
}