继续尝试搓 bpm 分析

感谢 ChatGPT 让我发现 librosa 库里面已经有一个现成的分析 bpm 的算法了,先计算 onset strength,然后用动态规划算法算 bpm,但有一个致命的问题就是这玩意对歌曲本身没有理解,找到一个 align 的就直接 commit 了,导致可能搞到一些 3/4 倍 bpm. 我觉得如果和昨天的算法结合,不仅可以提升精确度,而且可以实现查找 offset.

玄学调参

降低采样率发现效果好一些。

对噪声进行测试发现相关系数关于偏移呈现线性,于是以下补偿系数效果非常好:

1
2
mult = 1 / (midpoint - np.arange(min_ndx, max_ndx))
correl_midpoint_tmp = correl_midpoint_tmp * mult

补偿后可以安全地以 0.15 作为阈值排除噪声。

为了检测更高 bpm 的歌曲,把上限从 220 调到了 240.

在降采样的时候使用 max 比 mean 效果好。

但是……

我发现我能量分布算了之后直接丢掉了,用来跑 auto correlation 的是原音频数据!表现都比离散小波变换之后好!逆大天!

基于 GCD 的 BPM 计算

考虑对常用的比例进行试探,假如我们有一串数组 2d+x1, 3d+x2, 5d+x3…, 其中 xn 是很小的误差,我们起初不知道 2:3:5 这个比例,但可以分别假设第一个是 1d+rem, 2d+rem, …, 10d+rem, 然后计算后面的取余误差,最后采用误差最小且系数最小的一个即可。

但这样算出来的 BPM 会太大!

加一个 120-240 的限制,后续开放修改接口,然后以偏差的峰数而不是距离作为判断依据,就可以筛选出最合适的 bpm. 用任意一个峰计算的话误差可能比较大,但如果用最高峰计算,误差大概在 0.1 以内。

偏移计算

直接对 BPM 置信度峰值所在的窗口找 onset 最大的,这个是整首歌里面程序最能确定是「一拍」的地方,以这一拍的时值作为 offset. 发现需要给 onset 结果的 x 轴减去 1/32 秒,根据 ChatGPT, 可能是因为内部算法有 starting offset, 但 librosa.times_like 函数不能给出这个确切值,只是从 0 开始。

或许还是不加这个为好,keep it simple and stupid.

还是手贱打了个 1 / args.hop_length 上去。