Преобразование БПФ в спектрограмму
У меня есть аудиофайл, и я перебираю файл и беру 512 сэмплов на каждом шаге, а затем пропускаю их через FFT.
У меня есть данные, поскольку блок 514 плавает долго (используя ippsFFTFwd_RToCCS_32f_I IPP) с чередованием действительных и мнимых компонентов.
Моя проблема в том, что мне делать с этими комплексными числами, когда они у меня есть? На данный момент я делаю для каждого значения
const float realValue = buffer[(y * 2) + 0];
const float imagValue = buffer[(y * 2) + 1];
const float value = sqrt( (realValue * realValue) + (imagValue * imagValue) );
Это дает что-то немного пригодное для использования, но я бы предпочел какой-то способ вывести значения в диапазоне от 0 до 1. Проблема с тем, что он выше, состоит в том, что пики возвращаются примерно к 9 или более. Это означает, что вещи становятся ужасно насыщенными, и затем есть другие части спектрограммы, которые едва обнаруживаются, несмотря на то, что они кажутся достаточно сильными, когда я пропускаю звук через спектрограмму прослушивания. Я полностью признаю, что не уверен на 100%, что представляют собой данные, возвращаемые БПФ (кроме того, что он представляет значения частоты блока длиной 512 выборок, который я передаю). Особенно мне не хватает понимания того, что именно представляет собой целое число.
Любой совет и помощь будут высоко ценится!
Изменить: просто чтобы уточнить. Моя большая проблема заключается в том, что возвращаемые значения БПФ не имеют смысла без какого-либо представления о масштабах. Кто-то может указать мне на разработку такого масштаба?
Edit2: я получаю действительно красивые результаты, выполнив следующее:
size_t count2 = 0;
size_t max2 = kFFTSize + 2;
while( count2 < max2 )
{
const float realValue = buffer[(count2) + 0];
const float imagValue = buffer[(count2) + 1];
const float value = (log10f( sqrtf( (realValue * realValue) + (imagValue * imagValue) ) * rcpVerticalZoom ) + 1.0f) * 0.5f;
buffer[count2 >> 1] = value;
count2 += 2;
}
На мой взгляд, это выглядит даже лучше, чем большинство других реализаций спектрограмм, на которые я смотрел.
Что-то Сильно не так с тем, что я делаю?
5 ответов
Обычная вещь, которую нужно сделать, чтобы получить все видимые БПФ, это взять логарифм величины.
Итак, позиция выходного буфера говорит вам, какая частота была обнаружена. Величина (норма L2) комплексного числа говорит вам, насколько сильна была обнаруженная частота, а фаза (арктангенс) дает вам информацию, которая намного важнее в пространстве изображения, чем в пространстве звука. Поскольку БПФ дискретно, частоты идут от 0 до частоты Найквиста. В изображениях первый член (DC) обычно является самым большим, и поэтому хорошим кандидатом для использования в нормализации, если это ваша цель. Я не знаю, верно ли это для аудио (я сомневаюсь в этом)
Для каждого окна из 512 выборок вы вычисляете величину БПФ, как и раньше. Каждое значение представляет величину соответствующей частоты, присутствующей в сигнале.
mag
/\
|
| ! !
| ! ! !
+--!---!----!----!---!--> freq
0 Fs/2 Fs
Теперь нам нужно выяснить частоты.
Поскольку входной сигнал имеет реальные значения, БПФ является симметричным относительно середины (компонент Найквиста), причем первый член является компонентом постоянного тока. Зная частоту дискретизации сигнала Fs
частота Найквиста Fs/2. И поэтому для индекса k
соответствующая частота k*Fs/512
Таким образом, для каждого окна длиной 512 мы получаем величины на указанной частоте. Группа этих последовательных окон образует спектрограмму.
Просто чтобы люди знали, что я проделал МНОГО работы над этой проблемой. Главное, что я обнаружил, это то, что БПФ требует нормализации после этого.
Чтобы сделать это, вы усредняете все значения вашего вектора окна вместе, чтобы получить значение несколько меньше 1 (или 1, если вы используете прямоугольное окно). Затем вы делите это число на количество частотных бинов, которые у вас есть после преобразования FFT.
Наконец, вы делите фактическое число, возвращаемое БПФ на число нормализации. Ваши значения амплитуды теперь должны находиться в диапазоне от -Inf до 1. Вход и т. Д., Как вам угодно. Вы все еще будете работать с известным диапазоном.
Есть несколько вещей, которые, я думаю, вы найдете полезными.
Прямой FT будет иметь тенденцию давать большее число на выходе, чем на входе. Вы можете думать об этом как о всей интенсивности на определенной частоте, отображаемой в одном месте, а не распределенной по набору данных. Имеет ли это значение? Вероятно, не потому, что вы всегда можете масштабировать данные в соответствии с вашими потребностями. Однажды я написал целочисленную пару FFT/IFFT, и каждый проход требовал масштабирования для предотвращения переполнения целых чисел.
Реальные данные, которые вы вводите, преобразуются в нечто почти сложное. Оказывается, буфер [0] и буфер [n/2] являются реальными и независимыми. Здесь есть хорошее обсуждение.
Входными данными являются значения интенсивности звука, взятые во времени, с равным интервалом. Говорят, что они, соответственно, находятся во временной области. Говорят, что выходной сигнал FT находится в частотной области, потому что горизонтальная ось является частотой. Вертикальная шкала остается интенсивностью. Хотя это не очевидно из входных данных, во входных данных также есть информация о фазе. Хотя весь звук является синусоидальным, нет ничего, что фиксирует фазы синусоидальных волн. Эта информация о фазе появляется в частотной области как фазы отдельных комплексных чисел, но часто мы не заботимся об этом (и часто мы делаем это тоже!). Это зависит только от того, что вы делаете. Расчет
const float value = sqrt((realValue * realValue) + (imagValue * imagValue));
извлекает информацию об интенсивности, но отбрасывает информацию о фазе. Принятие логарифма, по сути, просто ослабляет большие пики.
Надеюсь, это полезно.
Если вы получаете странные результаты, то стоит проверить документацию библиотеки FFT, чтобы увидеть, как упакован вывод. Некоторые подпрограммы используют упакованный формат, в котором чередуются реальные / мнимые значения, или они могут начинаться с элемента N/2 и переходить в другое.
Для проверки работоспособности я бы предложил создать выборочные данные с известными характеристиками, например, Fs/2, Fs/4 (Fs = частота выборки) и сравнить выходные данные процедуры FFT с тем, что вы ожидаете. Попробуйте создать синус и косинус на одной и той же частоте, так как они должны иметь одинаковую величину в спектре, но иметь разные фазы (т.е. realValue / imagValue будет отличаться, но сумма квадратов должна быть одинаковой.
Если вы намереваетесь использовать FFT, тогда вам действительно нужно знать, как он работает математически, в противном случае вы, вероятно, столкнетесь с другими странными проблемами, такими как псевдонимы.