原理
在现在的喊麦上,人声消除是常用的一个功能,笔者测试过几款喊麦的系统,发现就人声消除这块,可以说效果是参差不齐。由于自己产品上也要有这个功能,就花了一些时间来研究了一下。下面就把研究的心得体会做个总结。
人声的声波波形在歌曲的两个声道是相同或者相似的,因此,我们可以采取两个声道相减的办法来消除立体声歌曲中的人声。
一个标准的人声消除过程如下:
步骤一:高通滤波器(High-pass filter)
步骤二:声道混合(Channel Mixer)
步骤三:低通滤波器(Low-pass filter)
其实,高通和通过滤波器的作用就是尽量让音乐的声音保留。在实际的操作中,有些简单的做法直接使用步骤2,省略到了步骤1和3,这样做的好处是操作简单,问题是人声音消除的不是很干净的。
我们一般把声道混合分成四个参数,分别为:
- 新左声道里原左声道所占的百分数a1;
- 新左声道里原右声道所占的百分数a2;
- 新右声道里原左声道所占的百分数b1;
- 新右声道里原右声道所占的百分数b2;
a1,a2,a3,a4这四个数的数值在-100到100之间。则新左声道采样值newLeft=a1Left/100+a2Right/100,新右声道采样值newRight=b1Left/100+b2Right/100。
如果想消除80%的人声,那么就将声道混合的数值设定如下:
100,-80, -80, 100;
如果想消除50%的人声,那么声道混合的四个数值为:
100, -50, -50, 100。
如果想消除100%的人声,那么声道混合的四个数值为:
100, -100, -100, 100。
这样,人声消除就很清楚了,接下来就是算法实现了。
python源码
在这里,我用python做了一个简单的仿真代码,可以实现简单的消除人声的效果。具体代码如下:
1 import numpy as np 2 from scipy.io import wavfile 3 from scipy import signal 4 5 def splitChannel(srcMusicFile): 6 # read wav file 7 sampleRate, musicData = wavfile.read(srcMusicFile) 8 9 left = [] 10 right = [] 11 for item in musicData: 12 left.append(item[0]) 13 right.append(item[1]) 14 15 mixed_data = np.array(left) - np.array(right) 16 wavfile.write('mixed_b.wav', sampleRate, mixed_data) 17 18 splitChannel("test.wav")
通过播放原始音乐和处理后的音乐,发现人声消除的效果还不错。笔者发现,这个消除的效果要和音乐有关系,对于那些立体声比较对称的音乐来说,这种方法效果还是非常不错的。针对有些特殊的音乐,这个就不那么好用了。市面上基本上使用的都是这种方法。
对于那些本想把音频直接挂到博客园上,没想到遇到了一些技术问题,只能把它们连同代码一同放到github上了,有需要的朋友可以进去看一下。github地址:GitHub - DyLanCao/CoolAdudio: my open source audio edit software
输出音频:
方案实现
仅仅有仿真是远远不够的,还要把方案做出了,笔者在国内一家蓝牙芯片上,实现了该功能,效果初步测试和上面的仿真的基本能够吻合。不过,要把效果做的更好,还要加一些料的。针对消不掉的人声音,直接加vad检测,针对检测到的人声做一下衰减,再过一下这个,效果那就是杠杠的了。案子涉及到产品保密,就不往网上挂了。有兴趣的朋友可以通过QQ交流。