我在一个语音数据集上进行数据增强,想要在时间域上拉伸/压缩每个音频文件。
我找到了以下三种方法来实现这一点,但我不知道哪一种是最佳或更优化的方法:
dimension = int(len(signal) * speed)res = librosa.effects.time_stretch(signal, speed)res = cv2.resize(signal, (1, dimension)).squeeze()res = skimage.transform.resize(signal, (dimension, 1)).squeeze()
然而,我发现librosa.effects.time_stretch
会在信号中添加不必要的回声(或类似的东西)。
所以,我的疑问是:这三种方法之间的主要区别是什么?有没有更好的方法来实现这一点?
回答:
librosa.effects.time_stretch(signal, speed) (文档)
本质上,这种方法使用stft
(短时傅里叶变换)转换信号,通过相位声码器进行拉伸,并使用逆stft
重建时间域信号。通常,使用这种方法时,会引入一些“相位感”,即一种金属声,因为相位无法100%重建。这可能是你所指的“回声”。
请注意,虽然这种方法有效地在时间域中拉伸音频(即输入和输出都在时间域),但实际工作是在频域中完成的。
cv2.resize(signal, (1, dimension)).squeeze() (文档)
这种方法只是使用双线性插值来插值给定的信号。这种方法适用于图像,但我认为它不适合音频信号。你听过结果了吗?它听起来像原信号只是快了/慢了吗?我猜不仅仅是节奏改变了,频率和其他效果可能也会改变。
skimage.transform.resize(signal, (dimension, 1)).squeeze() (文档)
同样,这也是为图像设计的,而不是声音。除了插值(默认使用1
阶的样条插值)外,该函数还对图像进行抗锯齿处理。请注意,这与避免音频混叠效应(Nyquist/混叠)无关,因此你可能应该通过传递anti_aliasing=False
来关闭它。同样,我认为结果可能不是你想要的(改变频率,其他伪像)。
该怎么办?
在我看来,你有几个选择。
-
如果你输入机器学习算法的东西最终是梅尔频谱图,你可以简单地将其视为图像,并使用skimage或opencv方法进行拉伸。频率范围将被保留。我在这篇音乐节奏估计论文中成功地使用了这种方法。
-
使用更好的时间拉伸库,例如rubberband。librosa很好,但其当前的时间尺度修改(TSM)算法并非最先进。要了解TSM算法的评论,请参见例如这篇文章。
-
忽略频率变化的事实,简单地在信号中定期添加0样本或定期从信号中删除样本(就像你的图像插值所做的那样)。如果你不进行太大的拉伸,这对于数据增强目的可能仍然有效。毕竟,如果音频内容具有更高或更低的频率,词语内容不会改变。
-
将信号重新采样到另一个采样频率,例如
44100 Hz -> 43000 Hz
或44100 Hz -> 46000 Hz
,使用像resampy这样的库,然后假装它仍然是44100 Hz。这仍然会改变频率,但至少你可以从resampy获得正确过滤结果的好处,以避免上述的混叠现象,否则会发生这种现象。