相同长度音频剪辑的不同 FFT 信号长度

Different FFT signal lengths for same length audio clips

目前我正在做一个项目,需要我挑选音频片段并根据它们的 FFT 结果(即频谱图)进行比较。我所有的音频剪辑都是 0.200 秒长,但是当我通过转换处理它们时,它们的长度不再相同。我用于转换的代码使用了 numpy 和 librosa 库:

def extractFFT(audioArr):
    fourierArr = []
    fourierComplex = []
    for x in range(len(audioArr)):
        y, sr = lb.load(audioArr[x])
        fourier = np.fft.fft(y)
        fourier = fourier.real
        fourierArr.append(fourier)
     return fourierArr

我只采用转换的实数部分,因为我还想通过不允许复数的 PCA 传递它。无论如何,我无法对这个 FFT 音频片段数组执行 LDA(线性判别分析)或 PCA,因为有些片段的长度不同。

我为 LDA 编写的代码如下,其中为长度为 4 的 frequencyArr 给出了标签:

def LDA(frequencyArr):
    splitMark = int(len(frequencyArr)*0.8)
    trainingData = frequencyArr[:splitMark]
    validationData = frequencyArr[splitMark:]
    labels = [1,1,2,2]

    lda = LinearDiscriminantAnalysis()
    lda.fit(trainingData,labels[:splitMark])

    print(f"prediction: {lda.predict(validationData)}")

这会引发以下值错误,来自 lda.fit(trainingData,labels[:splitMark]) 行:

ValueError: setting an array element with a sequence.

我知道这个错误源于数组不是一组二维形状,因为当 FFT 元素都等长并且代码按预期工作时我没有收到这个错误。

这与音频片段有关吗?转换后,一些音频片段的长度相等,而另一些则不同。如果有人能解释为什么这些相同长度的音频片段可以 return 不同长度的 FFT,那就太好了!

注意,它们通常只有几点不同,比如 3 个音频片段的 FFT 长度是 4410,而第 4 个是 4409。我知道我可以 trim 缩短长度到组中最小的长度,但我更喜欢一种不会遗漏任何值的更简洁的方法。

首先:不要只取变换结果的实部。这对你没有任何好处。使用功率 (r^2+i^2) 或幅度 (sqrt(power)) 获取频率仓的信号强度。

Does this have something to do with the audio clips? After the transform, some audio clips are of equal lengths, others are not. If someone could explain why these same length audio clips can return different length FFT's, that would be great!

它们的长度根本不一样。我敢打赌你们剪辑的样本数不完全相同。

y, sr = lb.load(audioArr[x]) 之后执行 print('sample count = {}'.format(len(y))),您很可能会看到不同的值(您自己已经说明了很多)。

正如您已经指出的那样,您当然可以简单地切断 min(len(y)) 处的信号,然后将其输入 FFT。但通常,要解决此问题,您要做的就是使用 discrete STFT, which has a fixed window size. This ensures same length input size to the FFT. You can use librosa's implementation 作为简单的起点。文档还解释了如何获取 magnitude/power.

所以代替:

y, sr = lb.load(audioArr[x])
fourier = np.fft.fft(y)
fourier = fourier.real
fourierArr.append(fourier)

你做到了:

y, sr = lb.load(audioArr[x])
# get the magnitudes
D = np.abs(librosa.stft(y, n_fft=4096))  # use 4096 as window length
fourierArr.append(D[0])                  # only use the first frame of the STFT

本质上,如果你对不同长度的输入使用傅里叶变换,你会得到不同长度的输出,这是LDA不能原谅的,当使用这个输出作为训练数据时。所以你必须确保你的输入具有相同的长度。最简单的方法是使用 STFT(或简单地将所有输入剪切为 min)。 IMO,这没有什么不干净的,如果您缺少几个样本,它不会对结果产生太大影响。