В предыдущей статье мы занимались разработкой фильтра с бесконечной импульсной характеристикой. В результате был получен готовый класс для фильтрации данных. Все казалось бы прекрасно, но… Но мне кажется, что этого недостаточно для дорогого читателя этого сайта :(. Для закрепления полученных знаний или просто так я решил написать еще одну статью, в которой будет дан конкретный пример работы с классом. За основу была взята статья «Работа со звуковыми файлами (libsndfile) (static link library)«. В этой статье (если кто не читал) описан способ работы со звуковыми «.wav» файлами используя библиотеку «libsndfile». Так вот, в этой статье я оставил строчку комментария, в которой было написано, что именно в том месте можно реализовать обработку звуковых данных. Итого, я переписал предыдущий код под наши нужды и получил следующий проект FilterIIR.

Стоит всего лишь скачать рабочий проект и запустить его в среде программирования Visual Studio 2005. Перейдем к разбору кода, ведь нас интересует алгоритм работы программы. Алгоритм следующий:

1. Открываем на чтение звуковой «.wav» файл (входной).
 

1
2
3
4
5
6
7
8
9
// Open the input file
if ((infile = sf_wchar_open(infilename, SFM_READ, &sfinfo)) 
    == NULL)
{
    printf("Not able to open input file %s.\n", infilename);
    puts(sf_strerror(NULL));
 
    return 1;
}

 
2. Создаем новый звуковой «.wav» файл (выходной).
 
1
2
3
4
5
6
7
8
9
// Open/create the output file
if ((outfile = sf_wchar_open(outfilename, SFM_WRITE, &sfinfo)) 
    == NULL)
{
    printf("Not able to open/create output file %s : %s\n", 
        outfilename, sf_strerror(NULL));
 
    return 1;
}

 
3. Вызываем главную функцию-обработчик звуковых данных.
 
1
2
// main sound processing function
apply_iir_filter(infile, outfile, sfinfo);

 
4.Закрываем входной (начальные звуковые данные) и выходной (обработанные звуковые данные) файлы.
 

1
2
3
// cosing input and output files
sf_close(infile);
sf_close(outfile);

 
Понятное дело, самое интересное содержит именно функция «apply_iir_filter». Её содержимое следующее:
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// main sound processing function
static void apply_iir_filter(SNDFILE *infile, SNDFILE *outfile, SF_INFO info)
{
    int     channels = info.channels;
 
    CFilterIIRStream *cfiir = new CFilterIIRStream[channels];
 
    float   *buf = new float[channels*BLOCK_SIZE];
    float   *bufTmpChan = new float[BLOCK_SIZE];
    int     k, m, readcount, writecount;
 
    // initializing each instanse of the iir filter
    for (m = 0; m < channels; m++)
        cfiir[m].Init(CFilterIIRStream::HPF, static_cast(0.1*M_PI));
 
    while ((readcount = static_cast(sf_readf_float(infile, buf, BLOCK_SIZE))) > 0)
    {
        // for each of channel
        for (m = 0; m < channels; m++)
        {
            // for each of sample (it can be a lot of channels...)
            for (k = 0 ; k < readcount ; k++)
                bufTmpChan[k] = buf[k*channels + m];
 
            // processing data from single channel could be made here!!!
            cfiir[m].Process(bufTmpChan, readcount);
 
            // if processed value is out of the bounds - just clip it
            for (k = 0; k < cfiir[m].GetFilteredDataLength(); k++)
            {
                if(bufTmpChan[k] > 1.0f)
                    bufTmpChan[k] = 1.0f;
                else if(bufTmpChan[k] < (-1.0f))
                    bufTmpChan[k] = -1.0f;
            }
 
            // pushing back processed data into original array
            for (k = 0; k < readcount; k++)
                buf[k*channels + m] = bufTmpChan[k];
        }
 
        writecount = static_cast(sf_writef_float(outfile, buf, readcount));
    }
 
    delete [] buf;
    delete [] bufTmpChan;
 
    delete [] cfiir;
 
    return;
}

 
Что же мы тут видим? Так как наш входной звуковой файл может содержать несколько каналов (моно, стерео, квадро…), соответственно нужно для каждого канала создать свой экземпляр фильтра, т.е. мы создаем массив фильтров размерности равной количеству каналов:
 

1
CFilterIIRStream *cfiir = new CFilterIIRStream[channels];

 
Дальше мы инициализируем каждый из фильтров для каждого из каналов. В данном случае все фильтры мы настраивае одинаково:
 

1
2
3
// initializing each instanse of the iir filter
for (m = 0; m < channels; m++)
    cfiir[m].Init(CFilterIIRStream::HPF, static_cast(0.1*M_PI));

 
Как можно заметить из данных строк, этот фильтр пропускает высокие частоты, а низкие «обрезает». HPF — high-pass filter или же «фильтр высокую пропускает» в неточном, но правильном переводе.

Дальше идет цикл получения данных из входного файла, обработки данных методом «Process», усечение значений данных, если они выходят за рамки допустимых значений и запись результата в выходной файл. Где что вроде бы и так понятно. Если я ошибаюсь — говорите, пишите, кричите! Я допишу…:)

 

Головна частина

Завантажити вихідний код можна тут.