Кривые Безье в обработке звука
Что общего между графикой и звуком? Продолжая разговор о расчёте цифровых фильтров, попробуем ответить на этот вопрос.
Оглавление
Введение
Этот материал продолжает предыдущую статью. Коротко напомню её главные выводы:
-
чтобы изменить громкость отдельных частотных составляющих звука, его нужно пропустить через цифровой фильтр
-
фильтрацию можно выполнить линейной свёрткой отсчётов входного сигнала с конечной импульсной характеристикой КИХ-фильтра
-
коэффициенты КИХ-фильтра вычисляют либо по готовым аналитическим формулам, либо обратным преобразованием Фурье от нужной амплитудно-частотной характеристики (АЧХ, в общем случае произвольной)
-
реальная АЧХ фильтра, полученного таким способом, отличается от желаемой (порой весьма заметно), однако это расхождение можно уменьшить разными приёмами, например с помощью так называемой оконной функции
Вспомним и вопрос, оставшийся без ответа в прошлой статье: как встроить в собственную программу обработку звука многополосным графическим эквалайзером? Для начала разберёмся, что это такое.
Графический эквалайзер
В 1990-е годы звуковые карты были роскошью, а запись собственного Audio CD казалась почти чудом: на студенческую стипендию удавалось купить лишь несколько чистых болванок CD-R, а сам пишущий привод стоил свыше 1000 у.е. Тогда мы с друзьями записывали песни на самодельный 4-дорожечный магнитофон. Компьютер с процессором Pentium стоял далеко не у каждого, а о многодорожечной цифровой записи можно было только мечтать. Устройства записи и обработки звука были в основном аналоговыми. Тем не менее уже тогда звук можно было оцифровать через звуковую карту, а первые звуковые редакторы получили возможность применять разные виды цифровой обработки (так называемые эффекты). Именно в то время я увлёкся этой темой и начал писать собственные программные модули обработки.
Среди таких программ была Cool Edit Pro (ныне Adobe Audition), и в ней есть эффект Graphic Equalizer. Он как раз обрабатывает звук эквалайзером на основе КИХ-фильтра (фильтра с конечной импульсной характеристикой). Как уже говорилось в предыдущей статье, этот тип фильтра выбран неслучайно: он не искажает фазу звука, поэтому при регулировке громкости в разных полосах частот почти не возникает неприятных на слух искажений (при условии симметричной импульсной характеристики, которую легко обеспечить при расчёте).
Аналоговые графические эквалайзеры в виде отдельных приборов существуют уже не одно десятилетие. О некоторых из них можно прочитать в статье. Выглядят они примерно так:

Эквалайзер в Cool Edit Pro предлагает схожий интерфейс:

Его основные элементы:
-
вертикальные регуляторы громкости для каждой частотной полосы
-
кнопки переключения числа полос (10, 20 или 30)
-
поле Gain для установки общей громкости
Отдельно выделим дополнительные элементы: поле “Accuracy“ (points) и кнопку “Graph actual response”. В поле задаётся длина фильтра (его импульсной характеристики) в отсчётах (точках), а кнопка выводит график реальной АЧХ получившегося фильтра. Вспомним ещё один важный вывод прошлой статьи:
Чем короче КИХ-фильтр, тем хуже он справляется со своей задачей на низких частотах. Иными словами, чем длиннее фильтр, тем точнее его реальная АЧХ в области низких частот совпадает с желаемой.
Когда с помощью регуляторов задана нужная АЧХ, обязательно нажмите “Graph actual response” и посмотрите, насколько график реальной АЧХ (тонкая голубая линия) отличается от желаемого (синяя заливка). Проверим утверждение о длине фильтра на примере ВЧ-фильтра, подавляющего низкие частоты. Нажмём “Reset All to Zero” (сбросить всё в ноль), расширим диапазон (Range) регуляторов до 100 дБ и попробуем настроить подавление звука ниже 100 Гц. После нажатия “Graph actual response” видно, что реальная АЧХ заметно отличается от желаемой: фильтр довольно слабо подавляет низкие частоты:

Увеличим длину фильтра до 2000 точек:

Результат лучше, но реальная АЧХ приближается к желаемой лишь при длине фильтра от 5000 точек. Даже при максимально допустимых 16384 (больше задать нельзя) итог далёк от идеала, особенно если в полосе непропускания нужно подавить сигнал как можно сильнее (здесь подавление увеличено до 90 дБ):

Не будем забывать, что обработка звука линейной свёрткой требует большого объёма вычислений, поэтому длину фильтра следует увеличивать осторожно. Современные процессоры с несколькими гигагерцами тактовой частоты вполне справляются с такой фильтрацией в реальном времени (прямо во время воспроизведения). На Pentium же даже фильтр в 1000 точек обрабатывал звуковой файл очень долго и годился лишь для офлайн-режима: запустили, подождали, сохранили результат в файл. Впрочем, пока отложим эту тему.
Шкала частот эквалайзера
Для простоты переключим эквалайзер в 10-полосный режим и посмотрим на частоты полос, в которых регулируется громкость:

В интерфейсе полосы расположены по горизонтали на равном расстоянии друг от друга, но частоты соседних полос различаются вдвое (Гц): 31, 62, 125, 250, 500, 1000, 2000 и так далее. Причина в том, что человек воспринимает высоту звука логарифмически: каждый подъём тона на одинаковый по ощущению интервал, то есть октаву, означает удвоение частоты. Ранее уже упоминалось, что единицы громкости, децибелы (дБ), также появились из-за логарифмического восприятия громкости на слух. Особенности восприятия высоты звука описывал ещё Пифагор; желающие могут прочитать об этом в книгах (названия некоторых указаны в конце статьи).
Для нашей задачи отсюда следует вот что:
Чтобы громкость было удобно регулировать в разных частотных областях, частоты полос нужно вычислять по логарифмической шкале.
Как мы помним из предыдущей статьи, график нужной АЧХ можно задать в частотной области и получить из него импульсную характеристику КИХ-фильтра с помощью обратного дискретного преобразования Фурье (БПФ). Допустим, требуется получить изменение громкости, показанное на рисунке выше. Запишем его в виде таблицы по полосам (верхняя строка – частота в Гц, нижняя – громкость в дБ):
|
31 |
62 |
125 |
250 |
500 |
1000 |
2000 |
4000 |
8000 |
16000 |
|
0 |
3 |
0 |
6 |
0 |
-3 |
-6 |
0 |
12 |
0 |
Следовательно, в частотной области нужно «нарисовать» примерно такой график:

Однако обратное преобразование Фурье выполняется над массивом, значения в котором расположены по линейной частотной шкале. Поэтому значения с логарифмической горизонтальной шкалы необходимо соответствующим образом перенести на линейную. Кроме того, значения АЧХ нужно перевести из децибел в абсолютные величины, что делается просто:
double dB2Gain(double dB)
{
return pow(10.0, dB / 20.0);
}
Эксперименты с ВЧ-фильтром, проведённые выше, наглядно подтверждают следующее утверждение:
Фильтр, у которого АЧХ имеет нулевую ширину переходов между частотными полосами (так называемый идеальный фильтр), существует лишь в теории: для него потребовался бы КИХ-фильтр бесконечной длины. На практике построить такой фильтр невозможно.
Приходится искать компромисс, например сделать переходы АЧХ между полосами более плавными, без резких участков и прямых углов. Как именно это сделано в Cool Edit Pro, неизвестно, но можно предположить, что разработчики стремились сделать желаемый график АЧХ (Requested frequency response) достаточно гладким, чтобы он как можно меньше отличался от реальной АЧХ получаемого фильтра.
Построение графика АЧХ
Когда более 25 лет назад я занимался обработкой звука и захотел написать собственный КИХ-эквалайзер, передо мной встал тот же вопрос:
Как задать график желаемой АЧХ так, чтобы после обратного дискретного преобразования Фурье получился фильтр, реальная АЧХ которого мало отличается от желаемой?
Очевидно, это должна быть гладкая кривая без прямых углов. Задача усложняется тем, что график не фиксирован и должен обладать дополнительными свойствами:
-
гибко «подстраиваться» под произвольные значения громкости в полосах (как положительные, так и отрицательные) при перемещении регуляторов
-
в переходах между полосами проходить строго по границам между ними (например, если в одной полосе значение отрицательное, а в соседней положительное, посередине между ними должен быть почти вертикальный участок)
-
в верхних и нижних точках полос проходить точно через заданное значение громкости (то есть без лишних «выбросов» за пределы нужных значений, которые возможны, например, при некоторых видах сплайн-интерполяции между опорными точками)
Рисуя на бумаге разные графики, я постепенно понял, что напоминают мне такие кривые. В середине 1990-х мне приходилось работать в редакторе Corel Draw, где есть удобный инструмент – кривые Безье (Bézier curves; сейчас он есть во многих графических редакторах). Он позволяет соединить множество опорных точек непрерывной кривой, а с помощью дополнительных направляющих точек (так называемых guide points) обеспечить её гладкость, меняя форму и кривизну участков между опорными точками:

Я решил попробовать эти кривые. Но если в редакторе все опорные и направляющие точки задаются и перемещаются вручную, то мне требовалась программа, которая сама расставляет все точки и строит по ним кривую.
Кривые Безье
Теории кривых Безье в сети посвящено немало материалов, а здесь их можно «покрутить» самостоятельно и увидеть, как выглядят кривые разного порядка и как на их форму влияют направляющие точки:

Нам же предстоит выяснить, как для нужного графика АЧХ задать базовые (узловые, или опорные) и направляющие точки (guide points). Возьмём приведённый выше пример АЧХ и отметим красными точками середины частотных полос, через которые график обязан пройти:

Это, очевидно, и есть опорные точки. Чтобы соединить их плавной кривой, я решил расположить направляющие точки по горизонтали строго посередине между опорными, а вертикальные координаты взять такими же, как у соответствующих опорных точек. На рисунке направляющие показаны зелёным и синим цветами:

Если каждую пару опорных точек соединить кривой Безье с соответствующими направляющими, получится примерно такая кривая:

Выглядит вполне прилично, с таким графиком уже можно работать. Но в каких единицах задавать координаты точек, чтобы итоговый график АЧХ получился в линейной шкале частот? Над этим пришлось поломать голову, пока не появилась такая идея:
-
Задаётся базовая частота первой опорной точки, то есть самой низкой частоты первой полосы.
-
Ширина полос задаётся в октавах.
-
Частоты остальных полос вычисляются по базовой частоте и расстоянию до них в октавах.
Например, чтобы получить 10-полосный эквалайзер
Короткие видео глазами нейрофизиолога: как объяснять сложное просто и увлекательно
Почему нейросети так часто ставят длинное тире?
Обзор второго издания «Искусства схемотехники: X-главы»
Колокольчик для кошки: болтать — не значит делать
Козодой: аэродинамика компромисса
«Универсальная вакцина защитила мышей от множества патогенов»: фантазия или шанс для будущих поколений
Можно ли заглянуть в «мозг» нейросети и понять, о чём она думает?
Зачем на взлётно-посадочных полосах делают поперечные борозды: что говорится в отчётах NASA