vocalremover网站测BPM的算法是什么?
缘起
我个人偶然发现的网站,用多了觉得这个网站测出来的BPM准确率还挺高的,几乎就没错过,就算有也只是差那么一点点(比我自己用librosa还有wave库要高,这两个准的时候很准,不准的时候差十万八千里),于是就想看看抄抄算法。
过程
查看源代码,发现有两个核心的js:AppBpmFinder
以及key-finder
。
先看AppBpmFinder
,搜索bpm,找到和bpm处理有关的代码。
我这里首先下的断点是最下面那个,运行,发现到这里的时候BPM已经测出来了,于是往上再设断点。
在这里设置断点后,运行,此时BPM还没测出来,单步执行下一个函数调用。
p.postMessage({ |
然后发现这里是post了采样率以及PCM数组的message到了key-finder
中。(所以为什么是在key-finder里面测BPM)
感觉快找到真相了(并不),贴一个上图用到的一些函数。
const ZB = 16e3 |
首先看看
I = Ug(I) |
Ug 这个函数首先判断 A 的 length 是否为 2 (对应音频是否为双声道),从一个二维数组 A 中取出第一行和第二行,分别赋值给 I 和 E,然后返回一个新的数组,它的每个元素是 I 和 E 对应位置元素的平均值。例如,如果 A 是:
[[1, 2, 3], |
那么 I 就是 [1, 2, 3]
,E 就是 [4, 5, 6]
,返回的数组就是 [2.5, 3.5, 4.5]
。
这段代码使用了 JavaScript 的 map 方法,它可以对一个数组的每个元素执行一个函数,并返回一个新的数组。这里的函数是 (C,g)=>.5 * (C + E[g])
,它的参数是 C 和 g,其中 C 是 I 的每个元素,g 是它的索引。函数的返回值是 C 和 E 在相同索引位置的元素的平均值,即 .5 * (C + E[g])
。
如果 A 的 length 为 1 ,则直接返回 A[0]。
所以这个函数的作用是将双声道音频的数据压缩(取平均)。
然后再看
I = Sg(I,E,C) |
这个函数的意思是,它可以将一个音频信号的采样率从 I 转换为 E(16000),返回一个新的 Float32Array,它的长度是原来的 I / E 倍。这个函数使用了一种简单的重采样算法,它将原始信号分成若干个等长的区间,每个区间的长度是 I / E,然后计算每个区间内的信号的平均值,作为新信号的一个采样点。这种算法可以有效地减少信号的长度,但是也会损失一些信号的细节和质量。
接下来就是关键了
const g = wg(I), B = yg(g); |
这里首先 wg
是一个箭头函数,它接受一个参数 A,然后返回 lB 的 arrayToVector 方法的结果,这个方法可以将一个数组转换成一个向量2,然后调用 yg
计算出BPM。
那么这个lb是何方神圣呢?
lB = new Essentia(h) |
lB 是一个 Essentia 类的实例,它是一个音频分析和处理的库1。
噔 噔 咚
结果
原来是Essentia库。
yg = A=>lB.PercivalBpmEstimator(A, 1024, 2048, 128, 128, 210, 50, ZB) |
所以这个网站是调用Essentia
库中PercivalBpmEstimator
的方法得到的BPM。
但是问题来了,我一开始调研的时候是有考虑过Essentia
库的,但是这个库在Windows上不能直接pip install
,所以我就没用这个库。
贴一个作者在不同issue下的回复。
You can run
pip install essentia
inside a Windows Subsystem for Linux (WSL). We do not provide pip wheels for Windows yet.Wheels aren’t yet supported for Windows (see #1157).
唉,把这个库安装,然后用上,实在是太难了。要么换一个操作系统再开发、打包等等进行一系列操作;要么花大把时间看着也不知到能不能成的英文教程(贴一下链接,真的看着就头大)去install,感觉都是非常吃力不讨好的。遂放弃。(反正我的项目中有究极无敌准确的人工测算法(不是)
世上无难事,只要肯放弃。