Страницы

Поиск по вопросам

среда, 22 января 2020 г.

Быстрое преобразование Фурье. Как выделить частоту ноты?

#java #аудио #fft #музыка


Заинтересовался реализацией выделением нот и распознаванием сигналов исходя из нот.
Вот если взять фортепиано, записать звук пары клавиш, после чего применить БПФ, на
выходе мы получим массив комплексных чисел, где амплитудой будет модуль комплексного
числа, а аргумент его фазой. Вот немного не понятно как выделить частоту ноты?
    


Ответы

Ответ 1



Преобразование фурье производится на комплексных числах. На вход для преобразования следует подавать в реальную часть амплитуду(величину сигнала), а в мнимую часть нуль. На выходе мы получим массив комплексных чисел, где амплитудой будет модуль комплексного числа, а аргумент его фазой. Каждый элемент массива представляет из себя одну гармонику, начиная с нулевой и завершая n-ой. Или это называется спектрами? В разных источниках на этот счёт разная информация. Спектры/гармоники отделены друг от друга дискретным шагом равным частотам дискретизации/количество отсчётов. Количество отсчётов равно количеству чисел амплитуд на входе, длине массивов входящих комплексных чисел, количеству длины входного сэмпла. В общем, количеству комплексных чисел на входе. Больше на входе отсчётов, больше разрешение по частоте на выходе. То есть, на выходе получив 1024 значений комплексных получаем 1024 значений амплитуда-фаза. Из этих значений, располагая массив по порядку и амплитуде можно получить нечто вроде визуализации амплитуды звука по частоте. Если дискретизация 44100Гц, а входной массив имеет длину 65536 то шаг между элементами массива на выходе получается 0.672912598 Гц. В целях распознавания человеческой речи подобная точность бессмысленна и избыточна. Путём генетического алгоритма (естественного отбора человеческого вида в условиях среды планеты Земля) оптимальным максимальным будет шаг между частотами от 1 до 9 Гц, то есть на вход желательно подавать максимум 8192 значений амплитуды-времени для частоты дискретизации 44100. Но прежде чем подавать эти данные на распознавание, с ними нужно ещё немного повозиться, но я не знаю как. Там что-то про окошки и что-то с функциями надо делать. Ничего не понял. UPD: Короче, вот код уточнения частоты основанный на смещении фаз в дополнение к фурье на java. /*числа пи*/ public static final double SinglePi = Math.PI; public static final double DoublePi = 2*Math.PI; /** * На вход подаётся два спектра, смещение между ними и частота. * @param spectrum0 * @param spectrum1 * @param shiftPerFrame * @param sampleRate * @return dictionary */ public static HashMap GetJoinedSpectrum( List spectrum0, List spectrum1, double shiftsPerFrame, double sampleRate) { int frameSize = spectrum0.size(); double frameTime = frameSize/sampleRate; double shiftTime = frameTime/shiftsPerFrame; double binToFrequancy = sampleRate/frameSize; HashMap dictionary = new HashMap(){ for (int bin = 0; bin < frameSize; bin++) { double omegaExpected = DoublePi*(bin*binToFrequancy); // ω=2πf double omegaActual = (spectrum1.get(bin).phase() - spectrum0.get(bin).phase())/shiftTime; // ω=∂φ/∂t double omegaDelta = Align(omegaActual - omegaExpected, DoublePi); // Δω=(∂ω + π)%2π - π double binDelta = omegaDelta/(DoublePi*binToFrequancy); double frequancyActual = (bin + binDelta)*binToFrequancy; double magnitude = spectrum1.get(bin).abs()+ spectrum0.get(bin).abs(); dictionary.put(frequancyActual, magnitude*(0.5 + Math.abs(binDelta))); } return dictionary; } public static double Align(double angle, double period) { int qpd = (int) (angle/period); if (qpd >= 0) qpd += qpd & 1; else qpd -= qpd & 1; angle -= period*qpd; return angle; } Ссылки: https://m.habrahabr.ru/post/247385/ http://introcs.cs.princeton.edu/java/32class/Complex.java

Комментариев нет:

Отправить комментарий