歌词逐字同步引擎
解决常见歌词视频问题——整行显示在歌手快速演唱(如快节奏段落或断句)时滞后于演唱者,通过将每个单词与声学起始点精确同步实现逐字显示。
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;
}