[控场AI]
· 7 分钟阅读· 3,901 字

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

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中完成,无需下载样本,且结果可复现。

rss source: Our music model ignores the key we ask for, so the browser measures the backing instead

节奏检测:锁定目标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项单元测试锁定数学逻辑,确保后续调整不会悄悄降低这一分数。

给生成式音频开发者的三条建议

  1. 把模型输出当作待测量的数据,而非契约。 提示词是请求,音频才是事实。
  2. 朴素提示词胜过精巧提示词。 一个朴素的循环和弦提示(“A | D | D | E, I–IV–IV–V in A major”)比详细的逐段时线被遵循得更好。
  3. 让用户先听到便宜的那一步。 在产生任何费用之前,Hum to Song会单独播放转录出的音符:“这是我们听到的旋律。”如果转录错了,没人会花钱去发现。

这套方法的通用价值在于:当生成模型无法精确控制时,后处理测量是可靠性的来源。对任何构建生成式音频产品的团队来说,把模型输出当数据分析,而不是当承诺相信,是更务实的工程路径。

#AI音乐生成#音频分析#浏览器端音频处理#Goertzel算法#哼唱转歌曲#生成式音频#旋律对齐
分享:

相关推荐