Различная длина сигнала БПФ для аудиоклипов одинаковой длины

В настоящее время я работаю над проектом, который требует от меня выбора аудиоклипов и сравнения их на основе результатов БПФ (т.е. спектрограммы). Все мои аудиоклипы имеют длину 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, который не допускает комплексных чисел. Несмотря на это, я не могу выполнить ни LDA (линейный дискриминантный анализ), ни PCA для этого массива БПФ аудиоклипов, поскольку некоторые из них имеют разную длину.

Код, который у меня есть для LDA, выглядит следующим образом, где метки даны для frequencyArr длины 4:

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.

Я знаю, что эта ошибка связана с тем, что массив не имеет заданной двумерной формы, поскольку я не получаю эту ошибку, когда все элементы БПФ имеют одинаковую длину, а код работает по назначению.

Это как-то связано с аудиоклипами? После преобразования некоторые аудиоклипы имеют одинаковую длину, а другие — нет. Если бы кто-нибудь мог объяснить, почему эти аудиоклипы одинаковой длины могут возвращать БПФ разной длины, это было бы здорово!

Обратите внимание, что обычно они отличаются всего на несколько пунктов, скажем, для 3 аудиоклипов длина БПФ составляет 4410, а для 4-го — 4409. Я знаю, что, вероятно, могу просто сократить длину до наименьшей длины из группы, но я бы предпочел более чистый метод, который не пропускает никаких значений.


person Andrew    schedule 30.07.2019    source источник


Ответы (1)


Прежде всего: не берите только реальную часть результата преобразования. Это не принесет вам никакой пользы. Используйте мощность (r^2+i^2) или величину (sqrt(power)), чтобы получить уровень сигнала для частотного бина.

Это как-то связано с аудиоклипами? После преобразования некоторые аудиоклипы имеют одинаковую длину, а другие — нет. Если бы кто-нибудь мог объяснить, почему эти аудиоклипы одинаковой длины могут возвращать БПФ разной длины, это было бы здорово!

Они просто не одинаковой длины. Бьюсь об заклад, количество образцов ваших клипов не совсем идентично.

После y, sr = lb.load(audioArr[x]) выполните print('sample count = {}'.format(len(y))), и вы, скорее всего, увидите другие значения (вы сами это указали).

Как вы уже заметили, конечно, вы можете просто отрезать сигнал на min(len(y)) и затем подать его в БПФ. Но обычно, чтобы обойти это, вы используете дискретный STFT с фиксированным размером окна. Это обеспечивает ту же длину входного размера для БПФ. Вы можете использовать реализацию librosa в качестве простой отправной точки. Документы также объясняют, как получить величину/мощность.

Итак, вместо:

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). ИМО, в этом нет ничего нечистого, и это не сильно повлияет на результаты, если вам не хватает пары образцов.

person Hendrik    schedule 30.07.2019
comment
Благодарим вас за разъяснение о разной длине аудио и о том, как разрешить эту ситуацию. Что касается включения воображаемой части преобразования, делает ли это включенный вами код? Буду ли я тогда тренироваться на величине/мощности аудиоклипа вместо FFT или STFT? Или реализация STFT устраняет проблему мнимых чисел? Извините, если этот вопрос неясен, я просто не понимаю, как мощность/величина играют роль после того, как я переключил его на STFT. - person Andrew; 30.07.2019
comment
Еще один вопрос/комментарий: какой параметр n_fft=4096 при загрузке аудио? Я просмотрел документацию librosa.core.load и не могу найти параметр, соответствующий этим критериям. - person Andrew; 30.07.2019
comment
Мои извинения. Этот параметр должен был войти в вызов stft (документы ). Я обновил образец кода. - person Hendrik; 30.07.2019
comment
Не беспокойтесь, оцените помощь/разъяснение! - person Andrew; 30.07.2019
comment
Извините, что беспокою еще раз, но есть ли причина, по которой я должен использовать только первый кадр, как указано, выполнив fourierArr.append(D[0])? Ни одна из других точек данных не имеет значения? Я посмотрел на печатаемые значения и увидел, что все они разные, поэтому я не понимаю, как вы пришли к выводу, что просто выбрали первый кадр и забыли остальные точки данных. Если бы это было проще, я мог бы открыть еще один вопрос для расширения, если это не дает достаточно информации. - person Andrew; 31.07.2019
comment
D[0] соответствует первому окну, т.е. первым 4096 выборкам. D[1] соответствует образцам с 1*hop_length по 1*hop_length+n_fft (hop_length=n_fft/2). Вы также можете использовать второе значение, на самом деле, если оно вам подходит, вы можете сделать среднее значение для каждого бина (используя np.mean с правой осью) или просто сбросить больше данных в свой LDA. Однако все, что не входит в первые 4096 семплов, будет дополнено нулями, потому что ваш сигнал короткий и может не повысить качество того, что вы хотите сделать. - person Hendrik; 31.07.2019