мячин.ком

Построение простейших фильтров. Часть 1.

Определение основных понятий

Следует начать с самого общего. Сначала про общее определение всяких штук связанных со звуком. Будем рассматривать дискретизированный звук - т.е. последовательность вещественных чисел:

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

Преобразование, не зависящие от будущего - это те преобразование, где отсчет результата ни коим образом не зависит от будущих отсчетов исходного звука. Зависит только от того, что на входе сейчас, и от того что было на входе в прошлом. Все преобразователи реального времени именно такие: они не умеют "предугадывать" будущее, что и понятно.

UPD: такие преобразования называются казуальными.

Линейное преобразование - это преобразование, для которого выполняется простое правило: преобразование суммы - это сумма преобразований. Более точно, на языке формул:

f(a+b) = f(a) + f(b);

f(r*a) = r*f(a), где r - это вещественное число, a и b - последовательности.

Преобразование, однородное по времени - это преобразование, которое действует одинаково в любой момент времени. Обычный фильтр - линеен и однороден по времени. Фленжер - линеен, но не однороден - взависимости от времени меняется фаза фленжера, меняется воздействие на звук.

UPD: такие преобразования называются инвариантными по времени.

Соединим эти классы - будем рассматривать преобразования, которые:

В результате получим определение фильтра. Ну не то, какое оно есть определение (потому что фильтры то изначально пришли скорее всего из непрерывной математики). А то, что мы будем иметь в виду под словом фильтр.

Каждый фильтр абсолютно точно и единственным при этом образом определяется импульсной характеристикой. Что такое импульсная характеристика(ИХ)? Это то, каким образом фильтр преобразует импульсный сигнал:

Поскольку фильтр не зависит от будущего, то до нулевого момента во времени импульсная характеристика нулевая. Поскольку фильтр однороден, импульсная характеристика в любой момент времени одинаковая. Поэтому можно говорить об импульсной характеристике фильтра.

Чем еще хороша импульсная характеристика? А тем, что имея её, мы всегда знаем как ведёт себя фильтр:

Видите? Мы просто применили ИХ к каждому импульсу входного сигнала. Но ведь звук - это и есть последовательность непрерывно идущих друг за другом импульсов! Значит имея ИХ, мы можем фильтровать. Имея фильтр - мы можем построить ИХ. Таким образом, множество ИХ и множество фильтров - это одно и то же, с взаимооднозначным соответствием.

То, что мы проделали сейчас с входным сигналом и ИХ называется сверткой. Обозначается звёздочкой. Например, f и g - это два сигнала, тогда f ж g - это свертка этих сигналов. Верно, например следующее:

f ж g = g ж f

f ж (g ж h) = (f ж g) ж h

Свертка, кстати по-английски convolution. Конволюционные алгоритмы реверберации - это значит, алгоритмы, построенные на свертках.

Следующее понятие, характеризующее фильтр - АЧХ. Амплитудно-частотная характеристика. Это кривая, которая показывает что делает фильтр с частотами. Как мы будем её строить? Очень просто. Мы будем давать на вход фильтру гармоническое колебание с определенной частотой и амплитудой 1. На выходе мы получим опять таки гармоническое колебание с той же частотой (есть такая математическая прелесть у гармонических колебаний - как их не сворачивай, получается снова гармоническое с той же частотой). Но амплитуда уже будет другая! Получается график зависимости амплитуды от частоты - то есть АЧХ фильтра.

В чем мы будем измерять частоту? В герцах? Пожалуй нет. Мы измерим длину волны в отсчетах. Разделим единицу на длину и получим частоту. Так, например, при частоте дискретизации 44100 Hz, частота 0.5 - означает 22050Hz, а частота 1 - это 44100 Hz. То есть наша единица измерения частоты - это часть от частоты дискретизации:

АЧХ не определяет фильтр так, как это делает ИХ. Поскольку фильтр вносит изменения не только в амплитуду гармонических колебаний, но и в фазу. (Вообще говоря, АЧХ правильнее строить в комплексной плоскости. Ту АЧХ, которую вы видите, это просто модуль комплексной АЧХ.) С другой стороны, множество АЧХ - это множество кривых. А кривых гораздо больше чем последовательностей (это бесконечности разных мощностей). Поэтому не на всякую АЧХ найдется фильтр. В общем, АЧХ характеризует фильтр, но совершенно его не определяет.

Вообще, это очень интересная задача: какие фильтры вообще бывают и как мы можем их определить. Среди множества всех фильтров множество фильтров, которые можно построить алгоритмически ничтожно! Слава богу, неалгоритмические фильтры - это абстракция, на них нельзя указать пальцем (это т.н. тезис Черча - здесь он будет звучать как "на что можно указать пальцем - то можно построить алгоритмически"). Но факт остается фактом: алгоритмы никогда не построят все ИХ. А среди всех алгоритмических фильтров опять же выделяются подклассы, которые легко изучать и с которыми можно что-то делать. В общем, где-то здесь начинаешь чувствовать ничтожность логического познания... Оставим эту безумную математику и перейдем ближе к программной реализации фильтров.

Буффер

Для того чтобы фильтр мог что-то сделать со звуком, он должен знать не только текущее значение на входе, но и должен "помнить" предыдущие значения. Для этого мы реализуем буффер. Буффер содержит последние n значений какого-то сигнала. Если буффер состоит из одного или двух отсчетов, то нужно завести одну-две переменые, которые будут сохранять значения. Если буффер большой, то логично завести т.н. циклический буффер: это массив, который заполняется циклически. При этом нужно помнить текущее положение в буффере и смещать его на каждом следующем отсчете, перезаписывая "хвост" буфера:

Собственно и всё. Этого знания о буффере нам будет достаточно для построения всевозможных фильтров и преобразований.

Фильтры конечной импульсной характеристики

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

... in5 in4 in3 in2 in1 input

Нам нужно построить фильтр, а именно вычислить текущее значение выхода - output. Получается примерно так (для буффера длиной 5):

output = f (input, in1, in2, in3, in4, in5)

Для того чтобы преобразование было линейным, необходимо чтобы функция f была линейна. То есть имела следующий вид:

f (input, in1, in2, in3, in4, in5) = a0input + a1in1 + a2in2 + a3in3 + a4in4 + a5in5

Разобравшись и подумав, нетрудно увидеть, что (a0 ... a5) - это есть ни что иное как ИХ этого фильтра. Таким образом, такие фильтры реализуют фильтрацию по импульсной характеристике определенного размера. То есть по конечной импульсной характеристике. Всё предельно просто. Как создать такой фильтр, чтобы он обладал нужной нам АЧХ? Через преобразование Фурье, но это тема отдельной статьи. Идём дальше.

Фильтры с двумя буфферами

Другой класс фильтров куда богаче и интереснее. Просто добавим еще один буффер. И будем записывать в буфферы не только входные, но и выходные значения. Вот так:

... in5 in4 in3 in2 in1 input
... out5 out4 out3 out2 out1 ?

И сейчас нам нужно найти вопросик - то есть текущее выходное значение. Суровые математические законы снова говорят нам о том, что для линейности функция должна иметь такой вид:

output = f (...) = a0input + a1in1 + a2in2 + a3in3 + a4in4 + a5in5 + b1out1 + b2out2 + b3out3 + b4out4 + b5out5

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

Биквадратные фильтры

Что такое биквадратный фильтр? Это фильтр с двумя буфферами длиной в 2 отсчета. Схема проста до невозможности: есть пять чисел, для того чтобы получить выходное значение, нужно взять их сумму с 5 весами. Всё.

in2 in1 input
out2 out1 ?

output = f (input, in1, in2, out1, out2) = a0input + a1in1 + a2in2 + b1out1 + b2out2

Определи эти пять чисел и получишь фильтр. Но пытаться найти фильтр подбирая эти 5 чисел - очень неблагодарная затея. Поскольку они не выражают никаких качественных параметров фильтра.

мячин.ком