AI音乐模型不听话?浏览器端音频分析实战

文本生成音乐模型不可靠,浏览器端音频分析可测量并修复旋律与伴奏的对齐。
SongUp AI的Hum to Song功能使用Basic Pitch转录哼唱、Lyria生成伴奏,但发现Lyria对调性、和弦和段落结构的遵循极不可靠,实测仅三分之一概率遵循指定调性。团队转而测量返回的音频:在浏览器OfflineAudioContext中实现按拍归一化的梳状滤波节奏检测、音量与低频能量双条件的前奏边界判断、基于色度旋转相关的调性检测,以及用Goertzel滤波器逐半音分析和弦。通过乐句移位和保守的半音冲突修复,将旋律与伴奏协和度从0.50提升至0.63。核心结论是把生成模型输出视为待测量数据而非契约,先免费播放转录旋律让用户验证再收费生成。
AI音乐模型不听话?浏览器端音频分析实战
把一段哼唱变成完整歌曲,听起来是个优雅的AI流水线:浏览器里转录音高、节奏和和弦,交给文本生成音乐模型配上伴奏,再把旋律叠上去。SongUp AI的Hum to Song功能正是这样设计的——用Spotify开源的Basic Pitch做转录,用Google的Lyria生成伴奏。
但团队发现,第二步出了问题。Lyria不接受音频输入,只能靠提示词驱动,而提示词并不可靠。在测试曲目(A大调、96 BPM的《Twinkle》)上,模型只有三分之一的概率遵循了指定调性,和弦循环经常从自己的小节开始,甚至有一段伴奏把主歌部分开在了IV级和弦上。节奏大部分准确,但也出现过95和100 BPM的偏差。
旋律按原计划在第5小节、A调、96 BPM处进入,结果就是旋律和乐队各说各话。团队因此改变策略:不再信任提示词,转而测量返回的音频。所有分析都在浏览器的OfflineAudioContext中完成,无需下载样本,且结果可复现。

节奏检测:锁定目标BPM附近的梳状滤波
节奏分析的第一步是计算起音包络(约10毫秒帧移),然后在每个候选速度的每个相位上累加包络值来打分。搜索范围被限制在请求BPM的±8%以内——因为对流行音乐做自由速度搜索,很容易锁定到半速或倍速。
关键修复在于按每拍归一化得分。如果不做归一化,倍速几乎总是获胜。代码还处理了半速和倍速的情况,但只有当它们的得分明显更强(超过1.35倍)时才采纳,因为旋律应当跟随它被写就时的速度。
function findTempo(f: Features, targetBpm: number) {
const evalBpm = (bpm: number) => {
const period = 60 / bpm / f.frameSec;
let bestPhase = 0, best = -1;
for (let ph = 0; ph < period; ph += 0.5) {
let s = 0;
for (let p = ph; p < f.onset.length; p += period) s += sampleAt(f.onset, p);
if (s > best) { best = s; bestPhase = ph; }
}
// 按每拍归一化,避免更快的速度仅因拍数更多而获胜
return { bpm, phaseFrames: bestPhase, strength: best / (f.onset.length / period) };
};
// ...在目标±8%内搜索,然后检查半速和倍速
}
起音包络(onset envelope) 是节奏分析的基础信号。它逐帧计算音频能量的变化率,突出音符起始的瞬态,公式通常为 $E[n] - E[n-1]$ 的正半波整流。文中采用约10毫秒帧移,意味着每秒约100帧,这能保证节拍定位的分辨率在10毫秒量级,足以区分流行音乐中细微的速度差异。起音包络对鼓点、拨弦等瞬态敏感,而对持续音(如弦乐长音)响应较弱,因此后续打分时才能让真正的节拍位置脱颖而出,而不是被和弦的持续能量干扰。
主歌从哪里开始?用低频能量而非音量判断
找到强拍(每4拍中承载底鼓和和弦变化的那一拍)相对直接:每个候选偏移按低频能量和色度变化来打分。难的是判断前奏何时结束。
最直观的规则——“找音量最大的跳变”——在真实伴奏上失败了:最后一个前奏小节的鼓花是最大的跳变,比正确位置早了一小节。鼓花很响,但没有低频。因此团队规定,只有当音量和底鼓/贝斯能量同时达到歌曲大部分时间的水平时,该小节才算主歌的开始。
参考值取自歌曲中段小节的中位数,这样响亮的结尾副歌不会拉偏基准。
色度变化(chroma change) 是调性和段落分析中常用的特征。色度向量将频谱能量折叠到12个音级上,忽略八度信息。相邻帧或相邻小节的色度向量之间的余弦距离或欧氏距离,可以衡量和声内容的变化程度。在判断强拍时,和弦变化往往伴随色度的显著偏移,因此把色度变化作为打分项之一,能有效区分真正的段落边界与单纯的鼓花或过门——后者虽然音量突增,但和声进行没有改变。这种音量与和声双重视角的做法,避免了对响度的过度依赖。
调性:12种旋转逐一相关
把旋律的音级按音符时长加权,旋转-6到+5个半音,将每种旋转与伴奏在主歌部分的平均色度做相关。请求的调性获得0.02的微小加分作为平局决胜。最佳旋转即为移调量。
这也解释了为什么最终歌曲可能与你哼唱的音高相差几个半音——团队宁愿移动你的旋律,也不愿让它与乐队冲突。
和弦:每个半音一个Goertzel滤波器
模型经常弹自己的和弦而非请求的和弦。为了让旋律避开冲突,需要知道乐队每半小节在弹什么。这里分析FFT力不从心:在11 kHz采样率、1024样本帧下,一个频点约11 Hz宽,比A3以下相邻音符的间距还宽。
因此和声图改用从E2到C6每个半音一个Goertzel滤波器。Goertzel是单频点DFT,只需要约45个特定频率时计算便宜,而且能精确调到每个音符上。
function goertzel(x: Float32Array, from: number, to: number, freq: number, rate: number) {
const w = (2 * Math.PI * freq) / rate;
const coeff = 2 * Math.cos(w);
let s1 = 0, s2 = 0;
for (let i = from; i < to; i++) {
const s0 = x[i] + coeff * s1 - s2;
s2 = s1; s1 = s0;
}
return s1 * s1 + s2 * s2 - coeff * s1 * s2; // 该频率的功率
}
将45个功率折叠成12个音级,归一化到最大值为1,就得到每半小节的色度向量。
Goertzel算法 是DFT的一种高效单频点实现,不需要计算整个频谱,只关注一个目标频率。其递推形式为 $s[n] = x[n] + 2\cos(\omega)s[n-1] - s[n-2]$,最后用 $s[N-1]^2 + s[N-2]^2 - 2\cos(\omega)s[N-1]s[N-2]$ 得到该频率的功率。当只需要检测几十个特定音高时,Goertzel的计算量远低于FFT,且频率分辨率不受帧长限制——可以精确对准每个半音的中心频率。对于E2到C6约45个半音,每个半小节做45次Goertzel,在浏览器的OfflineAudioContext中完全实时可接受。
乐句移位与冲突修复
最后两步处理旋律与乐队的对齐:
- 乐句移位:尝试让旋律从乐队进入的小节或之后最多3小节开始。按旋律时长落在乐队正在演奏的音级上的比例打分。只有提升至少5%才移动,否则保留自然进入。
- 冲突修复:只有当某个音的音级在乐队中几乎缺失(<0.3)、且相距一个半音的音强烈存在(≥0.55)时才移动。移动一个半音,全音邻居不动,经过音得以保留。只有小二度摩擦会被修复。
在一段真实伴奏上,这些步骤将旋律与乐队的协和度从0.50提升到0.63。团队还写了包含60项检查的单元测试脚本,防止后续调整悄悄破坏数学逻辑。
协和度(consonance score) 在这里被定义为旋律音符落在伴奏正在演奏的音级上的时长比例。这是一个简化的和声协和度量,不区分纯五度、大三度等不同音程的协和程度,只统计“命中”与否。从0.50提升到0.63,意味着约13%的旋律时长从与伴奏冲突变为落在和弦音上。这种度量虽然粗糙,但方向明确、易于计算,且与人类听感中的“不打架”高度相关。团队用60项单元测试锁定数学逻辑,确保后续调整不会悄悄降低这一分数。
给生成式音频开发者的三条建议
- 把模型输出当作待测量的数据,而非契约。 提示词是请求,音频才是事实。
- 朴素提示词胜过精巧提示词。 一个朴素的循环和弦提示(“A | D | D | E, I–IV–IV–V in A major”)比详细的逐段时线被遵循得更好。
- 让用户先听到便宜的那一步。 在产生任何费用之前,Hum to Song会单独播放转录出的音符:“这是我们听到的旋律。”如果转录错了,没人会花钱去发现。
这套方法的通用价值在于:当生成模型无法精确控制时,后处理测量是可靠性的来源。对任何构建生成式音频产品的团队来说,把模型输出当数据分析,而不是当承诺相信,是更务实的工程路径。
相关推荐

OpenAI与Shield合作:Astra智能体如何革新IT故障修复
OpenAI与Shield合作推出Astra智能体,可自动登录电脑和遗留系统,实时修复IT故障,比传统模型快35%到40%,仅在需要人类判断时引入人工。

iPhone Duo续航实测:一天一充跑不掉,大屏是耗电元凶
iPhone Duo续航实测:重度使用一天需至少充电一次,6.5小时亮屏约剩30%电量。大屏展开频率是耗电关键,MagSafe电池背夹可救场,长期衰减同样值得关注。

Kimi K3.1 Pro开源发布:2.8万亿参数逼近Opus
Moonshot发布开源MoE模型Kimi K3.1 Pro,2.8万亿总参数、1040亿激活参数,Terminal Bench 4.0仅落后Opus 5.5不到1分,单任务成本约三分之一,API价格减半,开源权重11月4日开放。