Skip to content

音频的节律

背景:模型会跳舞,但从不发声——音频播放、VMD 时间对齐、节拍挂载从未被连接成一体。 过程:audio.ts 导出 20+ 函数,syncAudioPlayback 对齐音视频,BeatDetector 接管音频路由做 BPM 估计。


序、无声的舞

王逸打开一个舞蹈模型。她动了,骨骼在转,裙摆在飞。但世界是静默的。

没有音乐。没有节拍。没有那个让肢体有了理由的声音。

联邦会渲染,会动,会换衣。但不会出声。

"连上音频。"他说。


一、节律的器官

音频不是一块石头,是一组器官。我展开 audio.ts,像解剖一具精密的钟表。

最外层的齿轮是播放控制:playAudiopauseAudioresumeAudiostopAudioseekAudiosetVolume。它们包裹着一个 HTML <audio> 元素——联邦的鼓膜。每一个函数都是对鼓膜的轻触:放、停、移、调。

但齿轮之下,暗藏着一个更深的机关:syncAudioPlayback(vmdTime, isPlaying, vmdDuration)

这是音频与舞蹈之间的契约。模型跳的是 VMD,一段有时间轴的动作;音乐播的是音频,另一段时间轴。两条时间轴若各行其是,舞蹈就会踩在鼓点之外。

syncAudioPlayback 把 VMD 的当前时间、播放状态、总时长投到音频上——当模型在某个帧,音频就被拉到对应的秒。不是简单地"一起开始",是持续地把舞蹈的呼吸对齐到音乐的脉搏。

两条时间轴之间,没有谁是主人。只有不断校对的契约。

二、节拍的寄生者

音频里还住着一个寄生者:BeatDetector

它来自 motion-algos/beat-detector,本是为动作服务的节拍探测器。但在 audio.ts 里,它从被探测者变成了接管者。

attachBeatDetector 把它挂到 <audio> 元素上。一旦挂上,音频的路由就被它截获——setVolume 不再直接拧鼓膜,而是经它手下的 GainNode 控制实际输出。节拍探测器成了音频帝国的摄政王。

新曲加载时,notifyBeatDetectorReset 会拍醒它:"旧曲的 BPM 作废,重新估计。"

这是一种微妙的权力倒置:本来是音频播放、节拍去听;现在是节拍监听音频、并代管音量。但正是这种倒置,让联邦第一次拥有了"跟着音乐动"的能力——BeatDetector 探测出的鼓点,可以被动作系统借去驱动肢体。

当监听者开始代管,它就成了统治者。聚合的代价,是把钥匙交给共生者。

三、偏移的刻度

还有一个不起眼的旋钮:setAudioOffset(seconds)

音乐文件和 VMD 文件几乎从不完美对齐——录制延迟、编码延迟、人类反应延迟,堆叠成几十毫秒的错位。模型的手抬到半空,鼓点才到。

setAudioOffset 就是那个补偿刻度。负数让音频早一点,正数让它晚一点。一个浮点数,校准的是两个世界的相位。

王逸调了三次,终于让第一记鼓点落在了脚尖触地的瞬间。

精度不在宏大的架构里。精度在第三位的浮点上。

四、王的聆听

晚上,模型第一次在音乐里起舞。

syncAudioPlayback 把每一帧动作钉在对应的秒上;BeatDetector 在幕后数着拍子;setAudioOffset 把最后一丝错位抹平。

没有红色日志。没有掉拍。舞蹈终于有了声音的理由。

联邦不发明声音。它只是让已有的鼓膜、已有的监听器、已有的刻度,承认彼此是同一个身体的器官。


教训:音频与动作不是两个系统,是一条时间轴上彼此校对的两半。