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("%fn", value);

}



sf_close (infile) ;

return 0 ;
} /* main */

1 ответов


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

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

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

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

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

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

теперь это вызывает еще одну проблему. Пока это информация обеспечивает идеальный алгоритм реконструкции сигнала это оставляет вас с проблемой, что частоты просачиваться в окружающие ячейки до некоторой степени. Чтобы решить эту проблему, некоторые математики (гораздо более умные, чем я) придумали концепцию функция окна. Функция window обеспечивает гораздо лучшую частотную изоляцию в частотной области, хотя и приводит к потере информации во временной области (т. е. невозможно полностью перестроить сигнал после вы использовали функцию окна, AFAIK).

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

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

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

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

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

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

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

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

и дышать

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

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

редактировать: в стороне я нашел kiss 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;
}