音乐检索(听歌识曲)实现过程

音乐检索(听歌识曲)实现过程

序言

  • 由于需要做无人机的声音识别,但是现有的做无人机声识别的成果不是很多,机缘巧合下在一篇论文中看到了一篇有关音乐识别的论文,就是这个shazam论文 ,该算法提出的时间比较早,也不是很复杂,而且已经商用了很久:shazam网站 ,当然,这个论文里面的是算法的初级版。所以,就萌生了想要实现它的想法,等复现后,视效果再考虑移植到对无人机声识别的过程中。

需要实现的功能

  • 输入一些音乐,提取这些音乐的声纹信息,存放于数据库中,用于识别音频片段
  • 输入一个音频片段,大约10s到20s左右,该音频片段相对于原曲,允许混杂一些噪音信号,但是要求原曲子的声纹信息已经存在于声纹信息库中

算法原理

这一部分,仔细看论文的话,其实还是很容易理解的,并不复杂,而且论文本身也描述的很详细了,距离实现,可能就差一层窗户纸。这里我自己做一份梳理和精要,算法主要分为两个部分,提取特征与检索方式:

  • 提取特征第一步(这里我只说明结果,并不解释为什么用这个特征,具体解释论文中很详细): 对音频做快速傅里叶变换,得到频谱信息;划分不同的频带,选取每一频带的幅度最大的频率点作为一个特征值的点保留下来。例如:总的频率范围是0-8KHz,那我如果平均划分8个区域:0-1,1-2,2-3...7-8,最后每一帧就会有八个极值点,其余的点就可以舍弃掉,后面我们只需要频谱的极值点。
  • 提取特征第二步: 计算哈希值。我们并不是直接将极值点信息不做处理直接放进数据库中,而是需要将极值点信息与其他信息做一些组合,保留时间,频率等特征。这里引入两个概念,两个概念:锚点($anchor\ point$)目标区域($target\ zone$)。锚点即当前点,锚点的目标区域是指与每一个锚点相对应的一片区域(例如时间线上排在后面的帧)。之后根据锚点与目标区域的相应特征提取出特征值,表征相应的信息,这个特征值的获取方式为:$[f1:f2:\Delta t:t1](\Delta t = t_1 - t_2)$,一帧信号可以有多个特征值,因为同一帧内本身就存在不止一个极值点,每个极值点都可以作为锚点,这主要取决于你的组合规则。
  • 之后,将所有提取出的信息存放进数据库中,之后检索时使用
  • 检索过程:检索过程比较简单,对待识别片段做同样的特征提取,得到一些特征值,然后用这些特征值去数据库中做匹配,最匹配的歌曲就是识别结果,其实,比较接近的算法就是倒排索引。可以用一个图说明:
    检索原理

  • 如图所示,上面的子图的纵轴是待识别音频片段,横轴为一数据库中完整音频,若该片段来自这个音频,互相匹配的特征值点便能构成一条倾斜的直线,显然线上每一点的横纵坐标之差(可以看作是音频片段的偏移值)相等。由此可以得到一个柱状图,横坐标是一首歌中存在的差值,纵轴表示差值的数量,可以看到差值为40的点最多,也就是说,如果这个音频片段属于这个歌曲,它最有可能是以40为起始点截取下来的。故,把40这个点位置上的数量,作为音频片段与它的匹配值。匹配值最高的就是最后的检索结果。

实现

准备

很费劲的一点就是由于C++中没有普遍的应用广泛的数字信号与音频处理库,但是自己从头造轮子这种事情做起来是很不值当的,于是第一步就是找到相关的第三方库,以下是我的选择:
- Eigen矩阵库,内置KissFFt库或者FFTW库,众所周知,fftw是速度最快的快速傅里叶运算库,不过我直接用的kissfft,没有做过多配置
- Boost, 这个无需多言了
- maximilian, 这个是一个C++的音频库,接口简单使用方便,值得一提的是,C++的音频库是比较多的
- MySQL Connector C++, 这个是mysql提供的C++接口,仿照JDBC标准实现的,在程序控制mysql的过程中,必不可少。尤其要注意win32和x64用的lib、dll是不一样的,这个错误我就犯过,查了好久才发现,而且这套接口问题很多,但是没办法,要连接就需要用,吐槽一句,java这点比C++好太多
- 这里多列一条,原本我打算使用levelDB这样的KV型数据库,因为正在看levelDB的源码,且想实际测试下它使用过程中的性能,无奈,其对windows平台的支持比较弱,而我只想找一个拿来就能用的数据库,于是腿而使用了mysql,后来使用中,mysql的性能有限,也成为了识别过程中的性能瓶颈,后续如果要优化,MySql肯定要被替换。

效果

  • 简单的建立了一个二十多首歌的小型库,截取其中某些歌曲的片段加入噪声,进行检测
  • 所有测试全部通过,都能够顺利检测出来,看到结果还是很惊讶的,毕竟,做这个只花了两三个星期的时间,而且还有优化空间,可见这一算法的优越性
  • 提取特征和识别的计算时间都很短,主要时间都花在了读写数据库上

Code

  • 代码整理之后会提供出来,以便移植和本地编译通过,之后还会整理出 how to build的文档

  • 2018.12.29号更新:程序我已经放在git上啦,地址在,之前的代码写很久了,现在看感觉质量不行。最近在写大论文,后面闲下来,我一定会重新写一遍的!立个flag。

  • 本人公众号如下,觉得文章对你有帮助的,就扫码关注下吧~
    公众号二维码
Comments are closed.