Appearance
音频的节律
背景:模型会跳舞,但从不发声——音频播放、VMD 时间对齐、节拍挂载从未被连接成一体。 过程:audio.ts 导出 20+ 函数,syncAudioPlayback 对齐音视频,BeatDetector 接管音频路由做 BPM 估计。
序、无声的舞
王逸打开一个舞蹈模型。她动了,骨骼在转,裙摆在飞。但世界是静默的。
没有音乐。没有节拍。没有那个让肢体有了理由的声音。
联邦会渲染,会动,会换衣。但不会出声。
"连上音频。"他说。
一、节律的器官
音频不是一块石头,是一组器官。我展开 audio.ts,像解剖一具精密的钟表。
最外层的齿轮是播放控制:playAudio、pauseAudio、resumeAudio、stopAudio、seekAudio、setVolume。它们包裹着一个 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 把最后一丝错位抹平。
没有红色日志。没有掉拍。舞蹈终于有了声音的理由。
联邦不发明声音。它只是让已有的鼓膜、已有的监听器、已有的刻度,承认彼此是同一个身体的器官。
教训:音频与动作不是两个系统,是一条时间轴上彼此校对的两半。