识别录音中的音符 - 第2部分 - Python

2 投票
2 回答
3036 浏览
提问于 2025-04-16 04:10

这是对这个问题的延续,详细内容可以在这里找到。

这是我用来获取样本的代码:

spf = wave.open(speech,'r')
sound_info = spf.readframes(-1)
sound_info = fromstring(sound_info, 'Int16')

sound_info的长度是194560,这个长度是采样率44100的4.4倍。声音文件的长度是2.2秒,那么sound_info的长度难道不是应该是它的两倍吗?

另外,我似乎只能找到关于为什么使用FFT来生成频率谱的信息。

我想把声音分成多个小段,分析每段的频率谱,而不是分析整个声音文件。


非常感谢大家的帮助。:)


这是基本的sound_info图表

plot(sound_info)

这是FFT图表

freq = [abs(x.real) for x in fft(sound_info)]
plot(freq)

2 个回答

0

别再重复造轮子了 :)

可以看看 http://librosa.github.io,特别是关于短时傅里叶变换(STFT)或者在你这种情况下更像是常数Q变换(CQT)的部分。

不过先说重点: 假设我们有一个来自音频文件的立体声信号(2个声道)。现在,我们把音频文件中两个声道的空间信息抛掉,创建一个平均声道(把两个声道的信号相加后除以2)。这样我们就得到了一个单声道的信号(1个声道)。因为这是数字信号,所以每个时间点叫做一个样本。

接下来就有趣了,我们把信号切成小块(叫做帧),通过连续取样本来实现(512或者2的倍数是标准值)。 对每一帧进行离散傅里叶变换(DFT),我们就得到了一个时间-频率表示,叫做声谱图。 关于其他概念(比如重叠等),可以在任何数字信号处理(DSP)书籍中找到,或者在这样的实验课程中查看: https://www.audiolabs-erlangen.de/content/05-fau/professor/00-mueller/02-teaching/2016s_apl/LabCourse_STFT.pdf

注意,DFT的频率轴是线性分布的。在西方音乐体系中,一个八度被分成12个半音,它们的中心频率是以对数方式分布的。可以查看上面的脚本,了解如何从线性STFT获得对数分布的频率轴的分箱策略。 不过,这种方法很基础,还有很多其他可能更好的方法。

回到你提到的音符识别问题。 首先:这是个很难的任务。 :) 如上所述,乐器发出的真实声音包含泛音。 而且,如果你想转录完整乐队演奏的音符,其他音乐家的干扰也会影响结果。

说到你可以尝试的方法: 现在很多人使用非负矩阵分解(NMF或类似的LDPCA)或神经网络来处理这个任务。 例如,NMF已经包含在scikit-learn中。 如果你想入门,我建议从NMF开始。只使用单音色的声音,也就是说,一次只让一个乐器演奏。用简单的衰减泛音结构初始化模板,看看会发生什么。

2

如果你的wav文件有两个声道,那么sound_info的长度就是2乘以采样率再乘以持续时间(以秒为单位)。这两个声道的数据是交替存放的,所以如果你把所有的值放进一个一维数组data里,那么一个声道的数据就是data[::2],另一个声道的数据就是data[1::2]


简单来说,平滑的函数可以用正弦波和余弦波的和来表示(它们的幅度和频率各不相同)。

快速傅里叶变换(FFT)可以把这个函数和那些正弦波和余弦波的幅度(系数)联系起来。也就是说,函数和系数序列之间是一一对应的关系。

如果一个声音样本主要是一个音符,它的FFT会有一个系数非常大(绝对值),而其他的系数则会很小。这个大系数对应着一个特定频率的正弦波,也就是这个音符的频率。

撰写回答