尝试改进 BPM 检测算法

昨天找到了一个利用离散小波变换实现的 BPM 检测算法,大致原理是用离散小波变换得到能量分布后,尝试在一个窗口区间(大概 3 秒)中将分布移动任意采样数后计算和自身的相关系数,相关系数大的采样数表示这个采样数可能是拍子的整数倍。

但这里有一些问题:因为窗口区间的长度没有和歌曲节拍契合,如果遇到变 bpm 的歌无法计算出其具体在哪里变化了 bpm;能量分布重合较大的可能会是整拍的简单分数倍;容易测出有小数点的结果;经过离散小波变换之后的结果很神秘。同时,现有的程序没有算偏移,但我需要这个信息,理论上是可以算出来的。

各环节波形的意义

相关结果会呈现一个基本对称的图案,但中间最高的地方并不是我们想要的频率,只是对应窗口长度。由此引出一个问题:由于最终用来算相关系数的数组是大于 0 的,显然重合部分越多相关系数越大,这样会不会引发对高 BPM 的错误偏好?

但我发现 cD_sum 本应有平均值 0,因为给它加的全部值都是 normalize 过的。尝试 log 一下各种中间结果。

发现一个很神奇的特性,cD_sum 的平均值是负的一万多,但最大值有 10^9 的级别,怀疑是脉冲式的。

原来是只 plot 了 abs. 傻呗了。

小波变换之后似乎还是对一些细节比较敏感,不过大致能反映能量分布。

尝试绘制了一下每个窗口的相关系数曲线,发现有些有很明显的峰结构,有的则没有,但原先值很大,求导之后依然很大。

尝试使用卷积把这些峰结构提取出来,发现卷积核太小反而能让信息丢失。不同歌曲的峰的形状不同,用卷积效果不好。

既然是峰,不如直接把计算最终 bpm 的方法改成取全局最大值。

normalize 管用!

对鼓点不太 align 的歌会搞到 4/3 倍之类的数字,解决方案是允许 bpm 特别高,由于 normalize 了,提高采样窗口后影响不会太大,但在 3/4 和 1/4 拍共存的时候这样就不会忽略掉 1/4 拍,从而可以正确识别 bpm.

找了一首全曲只有一点点节拍正常的歌,发现窗口小对这种歌有优势,但为了拟合这种歌会降低其他歌的准确率。补了一个对高 bpm 的线性削弱,但比较粗糙。

偏移读取

我发现这玩意不解决离散小波变换出来的数据太过傻逼的问题,就字面意义上没办法做出来。寄咯!到时候写制谱器还是用传统的打 bpm 方法吧,之前想的随着制谱调的方法太过超现实了一些。打算给游戏加个点击屏幕调偏移的机制,这样体验会好一些。