Intereting Posts
Анализ исходного файла C ++ после предварительной обработки int to char casting segmentation fault при создании программы подключения 4-х доски Подавить предупреждение GCC «дополнительные токены в конце директивы #include» Проектирование с использованием fork () и TCP-соединения в C Чтение и сохранение значений из CSV с использованием C с динамическим распределением Почему может быть разница между union * и struct *? ожидаемый неквалифицированный идентификатор до ‘->’ токена, как это исправить? Опция GCC -O2 нарушает эту небольшую программу или у меня есть неопределенное поведение gcc, неинициализированные глобальные переменные Хорошие практики программирования для определения макросов (#define) в C Разница между номером версии, младшим номером и номером выпуска Что определяет объем памяти? Генерация (очень) большой неповторяющейся целочисленной последовательности без предварительной перетасовки Неблокирующая обработка сокетов TCP. Как обнаружить состояние блокировки перед записью в сокет?

Анализ WAV-файлов C (libsndfile, fftw3)

Я пытаюсь разработать простое приложение C, которое может дать значение от 0 до 100 в определенном диапазоне частот в заданную метку времени в WAV-файле.

Пример: у меня частотный диапазон 44,1 кГц (обычный файл MP3), и я хочу разбить этот диапазон на n диапазонов (начиная с 0). Затем мне нужно получить амплитуду каждого диапазона, от 0 до 100.

То, что мне удалось:

Используя libsndfile, теперь я могу читать данные WAV-файла.

infile = sf_open(argv [1], SFM_READ, &sfinfo); float samples[sfinfo.frames]; sf_read_float(infile, samples, 1); 

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

Я нашел некоторую помощь здесь: https://stackoverflow.com/a/4371627/1141483

и посмотрел учебник FFTW здесь: http://www.fftw.org/fftw2_doc/fftw_2.html

Но поскольку я не уверен в поведении FFTW, я не знаю, как продвигаться отсюда.

И еще один вопрос, предполагая, что вы используете libsndfile: если вы вынуждаете чтение быть одноканальным (со стереофайлом), а затем читаете образцы. Будете ли вы тогда читать только половину образцов всего файла? Поскольку половина из них находится на канале 1 или автоматически отфильтровывает их?

Спасибо за тонну за вашу помощь.

EDIT: Мой код можно увидеть здесь:

 double blackman_harris(int n, int N){ double a0, a1, a2, a3, seg1, seg2, seg3, w_n; a0 = 0.35875; a1 = 0.48829; a2 = 0.14128; a3 = 0.01168; seg1 = a1 * (double) cos( ((double) 2 * (double) M_PI * (double) n) / ((double) N - (double) 1) ); seg2 = a2 * (double) cos( ((double) 4 * (double) M_PI * (double) n) / ((double) N - (double) 1) ); seg3 = a3 * (double) cos( ((double) 6 * (double) M_PI * (double) n) / ((double) N - (double) 1) ); w_n = a0 - seg1 + seg2 - seg3; return w_n; } int main (int argc, char * argv []) { char *infilename ; SNDFILE *infile = NULL ; FILE *outfile = NULL ; SF_INFO sfinfo ; infile = sf_open(argv [1], SFM_READ, &sfinfo); int N = pow(2, 10); fftw_complex results[N/2 +1]; double samples[N]; sf_read_double(infile, samples, 1); double normalizer; int k; for(k = 0; k < N;k++){ if(k == 0){ normalizer = blackman_harris(k, N); } else { normalizer = blackman_harris(k, N); } } normalizer = normalizer * (double) N/2; fftw_plan p = fftw_plan_dft_r2c_1d(N, samples, results, FFTW_ESTIMATE); fftw_execute(p); int i; for(i = 0; i < N/2 +1; i++){ double value = ((double) sqrtf(creal(results[i])*creal(results[i])+cimag(results[i])*cimag(results[i]))/normalizer); printf("%f\n", value); } sf_close (infile) ; return 0 ; } /* main */ 

Ну, все зависит от частотного диапазона, в котором вы находитесь. БПФ работает, беря 2 ^ n выборок и предоставляя вам 2 ^ (n-1) реальные и мнимые числа. Я должен признать, что я довольно туманна в отношении того, что именно представляют эти ценности (у меня есть друг, который обещал пройти все это со мной вместо кредита, который я ему сделал, когда у него были финансовые проблемы;)), кроме угол вокруг круга. Эффективно они предоставляют вам arccos параметра угла для синуса и косинуса для каждого частотного бункера, из которого исходные 2 ^ n выборок могут быть полностью восстановлены.

Во всяком случае, это имеет огромное преимущество в том, что вы можете рассчитать величину, взяв эвклидовое расстояние реальной и мнимой частей (sqrtf ((real * real) + (imag * imag))). Это дает вам ненормализованное значение расстояния. Это значение затем можно использовать для создания величины для каждой полосы частот.

Так что давайте возьмем заказ 10 БПФ (2 ^ 10). Вы вводите 1024 образца. Вы получаете FFT эти образцы, и вы возвращаете 512 мнимых и реальных значений (конкретный порядок этих значений зависит от используемого вами алгоритма FFT). Таким образом, это означает, что для аудиофайла 44,1 кГц каждый бит представляет собой 44100/512 Гц или ~ 86 Гц для каждого бункера.

Из этого следует выделить одно: если вы используете больше образцов (из того, что называется временным или пространственным доменом при работе с многомерными сигналами, такими как изображения), вы получаете лучшее частотное представление (в так называемой частотной области). Однако вы жертвуете одним за другого. Это как раз то, что происходит, и вам придется жить с ним.

В основном вам нужно будет настроить частотные модули и временное / пространственное разрешение, чтобы получить требуемые данные.

Сначала немного номенклатуры. Образцы времени 1024, о которых я упоминал ранее, называют вашим окном. Обычно, когда вы выполняете такой процесс, вы захотите сдвинуть окно на какую-то сумму, чтобы получить следующие 1024 отсчета, которые вы хотите FFT. Очевидным было бы сделать выборки 0-> 1023, затем 1024-> 2047 и т. Д. Это, к сожалению, не дает лучших результатов. В идеале вы хотите в какой-то степени перекрывать windows, чтобы с течением времени добиться более плавного изменения частоты. Чаще всего люди сдвигают окно на половину размера windows. т.е. ваше первое окно будет 0-> 1023 второго 512-> 1535 и т. д. и т. д.

Теперь это вызывает еще одну проблему. Хотя эта информация обеспечивает идеальную обратную коррекцию FFT-сигнала, она оставляет вам проблему, которая в какой-то степени течет в объемные бункеры. Чтобы решить эту проблему, некоторые математики (гораздо более умные, чем я) придумали концепцию оконной функции . Функция windows обеспечивает гораздо более высокую частотную изоляцию в частотной области, но приводит к потере информации во временной области (т. Е. Невозможно полностью перестроить сигнал после того, как вы использовали функцию windows AFAIK).

Теперь существуют различные типы оконных функций, начиная от прямоугольного windows (фактически ничего не делая с сигналом) до различных функций, которые обеспечивают гораздо лучшую частотную изоляцию (хотя некоторые могут также убивать окружающие частоты, которые могут вас заинтересовать !!). Существует, увы, ни один размер не подходит для всех, но я большой поклонник (для спектрограмм) функции windows черманмана-гарриса. Я думаю, что это дает лучшие результаты!

Однако, как я упоминал ранее, FFT предоставляет вам ненормализованный спектр. Чтобы нормализовать спектр (после вычисления евклидова расстояния), вам нужно разделить все значения на коэффициент нормировки (здесь я расскажу подробнее).

эта нормализация даст вам значение от 0 до 1. Таким образом, вы можете легко увеличить это значение на 100, чтобы получить шкалу от 0 до 100.

Это, однако, не там, где оно заканчивается. Спектр, который вы получаете от этого, довольно неудовлетворен. Это потому, что вы смотрите на величину с использованием линейной шкалы. К сожалению, человеческое ухо слышит, используя логарифмический масштаб. Это довольно часто вызывает проблемы с тем, как выглядит спектрограмма / спектр.

Чтобы обойти это, вам нужно преобразовать эти 0 в 1 значения (я назову его «x») в шкалу децибел. Стандартное преобразование – 20.0f * log10f (x) . Затем это даст вам значение, при котором 1 преобразуется в 0 и 0, преобразуется в -infinity. ваши величины теперь находятся в соответствующем логарифмическом масштабе. Однако это не всегда полезно.

На этом этапе вам нужно изучить исходную глубину бита образца. При 16-битной выборке вы получаете значение, которое находится между 32767 и -32768. Это означает, что ваш динамический диапазон – fabsf (20.0f * log10f (1.0f / 65536.0f)) или ~ 96.33dB. Итак, теперь мы имеем это значение.

Возьмите значения, которые мы получили от приведенного выше дБ. Добавьте это значение -96.33 к нему. Очевидно, максимальная амплитуда (0) теперь равна 96,33. Теперь он действует по тому же значению, и теперь у вас есть значение от -infinity до 1.0f. Закрепите нижний конец на 0, и теперь у вас есть диапазон от 0 до 1 и умножьте его на 100, и у вас есть конечный диапазон от 0 до 100.

И это намного больше монстра, чем я изначально планировал, но должен дать вам хорошее обоснование того, как создать хороший спектр / спектрограмму для входного сигнала.

и дышать

Дальнейшее чтение (для людей, отличных от оригинального плаката, который уже нашел его):

Преобразование БПФ в спектрограмму

Edit : В стороне я нашел поцелуй FFT гораздо проще в использовании, мой код для выполнения прямого fft выглядит следующим образом:

 CFFT::CFFT( unsigned int fftOrder ) : BaseFFT( fftOrder ) { mFFTSetupFwd = kiss_fftr_alloc( 1 << fftOrder, 0, NULL, NULL ); } bool CFFT::ForwardFFT( std::complex< float >* pOut, const float* pIn, unsigned int num ) { kiss_fftr( mFFTSetupFwd, pIn, (kiss_fft_cpx*)pOut ); return true; }