在WAV文件中查找音符
我想弄清楚怎么把WAV文件里的数据分离成各个音符。我是这样加载WAV文件的:
import scipy.io.wavfile as wavfile
rate, data = wavfile.read('scale.wav')
time = np.arange(len(data[:,0]))*1.0/rate
然后用下面的代码来绘图:
plt.plot(time, data[:,0])
plt.show()
这样我得到了这张图,图中是一个有八个音符的钢琴音阶。我想找到一种方法,把每个音符单独分开,这样我就能找出它的频率,知道正在演奏的是哪个音符。一旦我把音符分开了,后面的事情就好办了。
我试过找出最大值,但有太多的最大值了,得反复尝试才能找到我想要的那些,而且这个方法不太可靠,因为如果迭代次数太多,会把一些较小的峰值给去掉。如果能同时得到音符的时长就更好了。
编辑:所以这确实挺复杂的,正如你们所说的。我现在在想,我只想找到“极端”的峰值,然后找出这些峰值后面的极小值,作为我的音符,因为我们不需要太大一段数据就能找出它的频率。我的问题是,峰值太多了,很难只找到我想要的那些。有什么好主意吗?
3 个回答
你提到你有物理学的背景(这在原问题中很有帮助!),而且你已经知道如何通过一个单一的钢琴音调来获取频率和音符,使用的是快速傅里叶变换(FFT)。那么,接下来缺少的就是如何从一个在空间和时间上都局限的函数,转变为一个随时间变化的函数。一个常见的扩展方法是小波分析,其中的核心是
选择g(t-u)会根据你想如何处理随时间变化的信号而产生不同的结果。这样你得到的不是单一的频率,而是一个关于频率和时间的二维图。这会让你更容易从随时间变化的信号中提取音符。
当然,你也可以简单地设置一个窗口,在这个窗口内用FFT分析每个块,并提取出最大的频率——但这种方法不够稳健,需要手动调整,并且对更复杂的信号效果不好。
最简单也最有趣的事情就是计算你的数据的声谱图,这基本上就是把你数据的短段的频谱画出来,并且与时间进行对比。记得把频率的刻度设为对数,因为钢琴上音符的频率是以指数方式分布的。在Python中,你可以使用specgram这个函数来计算声谱图,它是matplotlib库的一部分。你可以看看这个谷歌图片搜索,看看不同类型音乐的声谱图长什么样。此外,还有一些可以播放MP3/WAV并带有可视化插件的电脑程序,我记得十多年前的Winamp就有实时播放声谱图的功能。
这确实是个有趣的练习,但我得提醒你,如果你想用这个技术自动转录某段音乐的音符,那可真是个难题,科学家们研究了很多年。一个问题是大多数乐器会产生很多谐波,这会让任何自动寻找音符的算法感到困惑。而且如果音乐中有人的声音或打击乐器,那就更麻烦了,因为这些会产生很多宽频噪音(尤其是字母's'和高帽音),几乎让其他音符无法识别。
如果你想更高级一点,可以看看Q变换(参见维基百科及其引用的论文)。你可以把它看作是一种声谱图,但在频率轴上的分箱是以对数方式排列的(例如,钢琴音阶上的每个半音或四分音都有一个箱子)。这种方法相比标准声谱图的好处在于每个音符都有固定数量的频率箱,而线性频率刻度在低音符上箱子少,高音符上箱子多。我不确定这是否在numpy中可用,你可能需要自己写代码。
为了给大家更新一下,这里是我们最终使用的代码。它通过对声谱图的列进行平均,来找出音符的位置,然后再利用谐波来确定这个音符的主要频率。接着,我们使用abjab工具把音符绘制在乐谱上。虽然不是完美的,但在单簧管的音阶上效果不错。
我们使用了audacity软件来去除输入音频中的噪音,有时候还会放大声音。