- PVSM.RU - https://www.pvsm.ru -

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3

Всем привет!
Вы читаете третью часть статьи про создание VST-синтезатора на С#. В предыдущих частях был рассмотрен SDK и библиотеки для создания VST [1] плагинов, рассмотрено программирование осциллятора и ADSR-огибающей для управления амплитудой сигнала.
В этой части я расскажу, как рассчитать и закодить фильтр частот, без которого не обходится ни один синтезатор. А без эквалайзера немыслима обработка звука.
Будет рассмотрен исходный код и применение эквалайзера из библиотеки NAudio [2] (библиотека для работы со звуком под .NET).
Внимание — будет много матана [3] — будем рассчитывать формулы для коэффициентов фильтра.

Исходный код написанного мною синтезатора [4] доступен на GitHub'е [5].

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 1

Скриншот VST плагина-эквалайзера Fab Filter Pro Q

Цикл статей

  1. Понимаем и пишем VSTi синтезатор на C# WPF [6]
  2. ADSR-огибающая сигнала [7]
  3. Частотный фильтр Баттервота
  4. Продолжение следует...

Оглавление

  1. Эквалайзер [8]
  2. Фильтрация через преобразование Фурье [9]
  3. Цифровые фильтры [10]
  4. Почему фильтр Баттервота? [11]
  5. Вывод формулы фильтра НЧ [12]
  6. Вывод формулы фильтра ВЧ и полосового фильтра [13]
  7. Программирование фильтра Баттервота по полученным формулам [14]
  8. Полосовой эквалайзер в библиотеке NAudio [15]
  9. Программы для рассчета фильтров [16]
  10. Список литературы [17]

Эквалайзер

Часто при обработке звука мы хотим изменить его характер/окраску/тембр. Сделать звук более басовым, убрать верхние частоты, или наоборот, сделать звук "прозрачным", оставив лишь середину и верха. Уверен, многие люди, не работавшие с обработкой звука, знают что такое эквалайзер [18] — они есть акустических колонках, музыкальных центрах, магнитофонах, плеерах, и т.д. Эквалайзер — это набор фильтров, каждый из которых изменяет амплитуду сигнала в его выбранной полосе частот. На бытовых колонках это, обычно, 2-3 крутилки — низкие частоты, средние и верха, с фиксированными полосами частот.

В Winamp'овском эквалайзере уже есть 10 заранее определенных полос.

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 2

Скриншот эквалайзера в плеере Winamp

В мире обработки звука существует множество плагинов-эквалайзеров, на любой вкус и цвет. Плагин Fab Filter Pro Q (скриншот в начале статьи) — это графический эквалайзер, позволяющий создавать большое число полос и редактировать их параметры.

Каждая полоса в эквалайзере — это, по сути, фильтр частот. Фильтры частот изменяют тембральные/частотные характеристики сигнала. В электронике существуют много типов и классификаций фильтров, с соответствующими характеристиками и параметрами — смотрим википедию [19].
Мы рассмотрим и запрограммируем самые простые фильтры: НЧ, ВЧ и полосовой фильтры.

Фильтрация через преобразование Фурье

По идее, вам никто не мешает делать с сигналом дискретное преобразование Фурье [20], обработать частоты и затем сделать обратное преобразование.
Если не думать над реализацией ДПФ, то такой подход я бы назвал достаточно интуитивным и простым в программировании (опять же, если взять ДПФ из какой-нибудь либы и не кодить самому).
Минусы подхода — во-первых, ДПФ принимает на вход массив из семплов, размер которого является степенью двойки. Это значит, что выходной сигнал уже будет с задержкой. Во вторых, каждый 512-й семпл мы будем производить данный алгоритм: ДПФ, обработка частот сигнала, обратное ДФП. Это не малые вычисления. В-третьих, есть еще минусы и тонкости, которые знают адепты цифровой обработки сигналов.

Мы не будем рассматривать применение ДПФ, а заглянем в теорию цифровых фильтров; напишем фильтр, который обрабатывает значения семплов и имеет линейную вычислительную сложность [21] в зависимости от длины входящего массива семплов.

Цифровые фильтры

Большую часть информации и вывод формул я взял из книги Digital Signal Processing: A Practical Approach [22] — очень рекомендую, она есть в русской версии — Цифровая обработка сигналов. Практический подход [23], заинтересованные найдут PDF в сети.

Хочу сделать важное замечание. Тема построения и рассчитывания фильтра действительно очень сложна, содержит массу тонкостей и нюансов, требует знания и понимания теории. В этой статье я покажу, как рассчитать формулы фильтра Баттервота, чтобы у читателя возникло понимание, откуда выводятся эти формулы. Но почему именно такие исходные формулы, почему именно такие замены — можно понять лишь погрузившись в глубокую теорию цифровой обработки сигналов.
Когда я начинал гуглить код фильтров, я сразу находил множество непонятного математического кода, и хотелось хоть чуть-чуть понять, откуда берутся такие рассчетные формулы. Осциллятор, огибающая, дилей — понимание и программирование работы этих составляющих лично мне кажется интуитивным, но только не фильтров. Этой статьей я хочу пробудить интерес к цифровой обработке сигналов) Буду рад, если возникнет желание разобраться в этой теме более основательно.

Вам нужно знать (хотя бы немножко) такие термины как свертка [24], импульсная характеристика фильтра [25], передаточная функция фильтра [26].

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 3

Аппроксимация АЧХ [27] идеального фильтра (картинка из советского учебника, не нашел исходник)

Фильтр изменяет сигнал, "убирая" в нем выбранные частоты. Существующие фильтры не идеальны. Полоса пропускания — полоса частот, которую фильтр "не затрагивает" (на графике есть некоторая Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 4 изменения — особенность неидеального представленного фильтра). Полоса подавления — полоса нежелательных частот. В полосе перехода происходит спад частот. Естественно, фильтр ближе к "идеальному" тем, насколько меньше он искажает полосу пропускания, насколько сильно он подавляет частоты в полосе подавления и насколько узка полоса перехода. Есть разные "приближения" фильтров — фильтр Чебышёва, Баттервота, и так далее — их вы найдете в книжках и на просторах сети.

Почему фильтр Баттервота?

Все очень просто, АЧХ фильтра Баттерворта максимально гладкая на частотах полосы пропускания — имхо, важнее всего не испортить сигнал в полосе пропускания.

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 5

Логарифмическая АЧХ для фильтров Баттерворта нижних частот разных порядков (скриншот взят с Википедии)

Вывод формулы фильтра НЧ

Передаточная функция [26] для фильтра Баттервота на s-плоскости [28] записывается следующими формулами:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 6 при четных n и
Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 7 при нечетных n

Здесь n — порядок фильтра: амплитуда на частоте среза w равна -3n dB, амплитудно-частотная характеристика затухает на −6n dB на октаву.

Чтобы получить сверточные коэффициенты, нужно получить передаточную функцию на z-плоскости [29] в виде

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 8

Найдем передаточную функцию для фильтра второго порядка (затухание на -6 dB на октаву), подставляем в формулу для H(s) n = 2:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 9

Тогда свертка для фильтра будет выглядеть так:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 10

Пусть нам заданы частота среза w (на которой амплитуда сигнала будет -3 dB) и Fc — частота дискретизации (число семплов в секунду), в герцах.

В формуле нужно использовать денормированные частоты, т.е. произвести замену (в полосовом фильтре будут две частоты w1 и w2, определяющие полосу пропускания):

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 11

Если мы хотим рассчитывать НЧ-фильтр, то нужно сделать преобразование — произвести замену параметра s в передаточной функции:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 12

Для рассчета других типов фильтров (ВЧ, полосового, режекторного) нужно делать другие замены. Они рассмотрены в книге Цифровая обработка сигналов. Практический подход [23] в части 8.8.2 и далее в статье.

Далее, для перехода в z-плоскость делаем замену:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 13

Для аналитических рассчетов я использовал пакет Mathematica.

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 14

Нужно получить числитель и знаменатель в виде полиномов от z. Приведем слагаемые знаменателя H(z) к общему знаменателю. Для этого найдем наибольший общий делитель (НОД, GCD) [30] слагаемых знаменателя и поделим на него числитель и знаменатель исходной функции H(z).

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 15

Найдем коэффициенты при степенях у полученных полиномов, используя функцию CoefficientList:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 16

Если все делать честно, то, по условию, a0 должен быть равен 1 — поделим на a0 все коэффициенты (для кодинга будем использовать предыдущие формулы без деления):

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 17

Вывод формулы фильтра ВЧ и полосового фильтра

Вывод формул для ВЧ-фильтра аналогичен НЧ-фильтру с другим преобразованием:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 18

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 19

Для вывода формул полосового фильтра применяется преобразование:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 20

Если производить замену, то степень полиномов в числителе и знаменателе H(z) удвоится (в замене есть s^2), значит порядок фильтра увеличится вдвое. Поэтому изначально используем функцию H(s) для n = 1:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 21

Программирование фильтра Баттервота по полученным формулам

Фильтр будет иметь 2 параметра: тип фильтра (НЧ, ВЧ, полосовой) и частота среза w. Для полосового фильтра будем рассматривать частоту среза как частоту посередине полосы пропускания. Полосу пропускания же определим как отрезок частот [w — w/4, w + w/4] (можно придумать здесь более сложный и логичный здесь логарифмический закон, на ваше усмотрение).

Пусть мы определили коэффициенты b0, b1, b2, a1 и a2 (a0 по условию равен 1) по рассчитанным формулам. Алгоритм работы фильтра сводится к свертке, которая делается последовательно для каждого семпла:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 22

y(n) — это новое значение семпла, которое нужно рассчитать. x(n) — текущее значение семпла, соответственно y(n-1) и y(n-2) — предыдущие 2 рассчитанных семпла, а x(n-1) и x(n-2) — предыдущие входные значения семплов.
Нужно организовать запоминание предыдущих семплом.
Не будем мудрить с циклическими буферами, сделаем просто и понятно: два массива из трех элементов. Каждый раз будем "проталкивать" новые значения в этот массив, последовательно копируя более старые значения семплов.

Получаем простой класс:

class BiquadConvolutionTable
{
    public double B0, B1, B2, A1, A2;

    private readonly double[] _x = new double[3];
    private readonly double[] _y = new double[3];

    public double Process(double s)
    {
        // "сдвигаем" предыдущие семплы
        _x[2] = _x[1];
        _x[1] = _x[0];
        _x[0] = s;

        _y[2] = _y[1];
        _y[1] = _y[0];

        // свертка
        _y[0] = B0 * _x[0] + B1 * _x[1] + B2 * _x[2] - A1 * _y[1] - A2 * _y[2];

        return _y[0];
    }
}

Напишем каркасный класс для фильтра (смотри архитектуру синтезатора в первой статье [6]).
Класс BiquadConvolutionTable работает с одним сигналом, т.е. с одним каналом — моно. Поэтому нам нужны две BiquadConvolutionTable — для левого и правого каналов.
Чтобы корректно применить фильтр, нужно последовательно, для всех семплов входящей последовательности применить функцию BiquadConvolutionTable.Process и заполнить результирующий массив семплов.
Рассчетом коэффициентов для BiquadConvolutionTable будет заниматься функция CalculateCoefficients.

public enum EFilterPass
{
    None,

    LowPass,
    HiPass,
    BandPass
}

public class ButterworthFilter : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor
{
    private readonly BiquadConvolutionTable _tablel;
    private readonly BiquadConvolutionTable _tabler;

    public EnumParameter<EFilterPass> FilterType { get; private set; }
    public FrequencyParameter CutoffFrequency { get; private set; }

    public ButterworthFilter(AudioProcessor audioProcessor) : base(audioProcessor)
    {
        _tablel = new BiquadConvolutionTable();
        _tabler = new BiquadConvolutionTable();
    }

    public override IEnumerable<Parameter> CreateParameters(string parameterPrefix)
    {
        FilterType = new EnumParameter<EFilterPass>(parameterPrefix + "Pass", "Filter Type", "Filter", false);
        CutoffFrequency = new FrequencyParameter(parameterPrefix + "Cutoff", "Filter Cutoff Frequency", "Cutoff");

        return new List<Parameter> {FilterType, CutoffFrequency};
    }

    public void Process(IAudioStream stream)
    {
        if (FilterType.Value == EFilterPass.None)
            return;

        var count = Processor.CurrentStreamLenght;
        var lc = stream.Channels[0];
        var rc = stream.Channels[1];
        for (int i = 0; i < count; ++i)
        {
            var cutoff = CutoffFrequency.Value;
            CalculateCoefficients(cutoff);

            var ls = _tablel.Process(lc.Samples[i]);
            lc.Samples[i] = ls;

            var rs = _tabler.Process(rc.Samples[i]);
            rc.Samples[i] = rs;
        }
    }

    private void CalculateCoefficients(double cutoff)
    {
        ...
    }
}

Функция CalculateCoefficients вызывается каждый раз в цикле — зачем? В следующей статье я расскажу про модуляцию (изменение во времени) параметров, и поэтому, частота среза может меняться, а значит, нужно перерассчитывать коэффициенты. Конечно, по-трушному, нужно подписаться на изменение частоты среза и уже в обработчике рассчитывать коэффициенты. Но в этих статьях я оптимизами заниматься не буду, цель — закодить фильтр.

Осталось закодить функцию CalculateCoefficients по рассчитаным формулам для коэффициентов.
Вспомним, что нужно использовать денормированные частоты, т.е. произвести замену:

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 23

Списываем все формулы для коэффициентов b0, b1, b2, a0, a1, a2. После рассчетов нужно поделить все коэффициенты на a0, чтобы a0 стало равно 1.

private double TransformFrequency(double w)
{
    return Math.Tan(Math.PI * w / Processor.SampleRate);
}

private void CalculateCoefficients(double cutoff)
{
    double b0, b1, b2, a0, a1, a2;

    switch (FilterType.Value)
    {
        case EFilterPass.LowPass:
        {
            var w = TransformFrequency(cutoff);

            a0 = 1 + Math.Sqrt(2) * w + w * w;
            a1 = -2 + 2 * w * w;
            a2 = 1 - Math.Sqrt(2) * w + w * w;

            b0 = w * w;
            b1 = 2 * w * w;
            b2 = w * w;
        }

            break;

        case EFilterPass.HiPass:
        {
            var w = TransformFrequency(cutoff);

            a0 = 1 + Math.Sqrt(2) * w + w * w;
            a1 = -2 + 2 * w * w;
            a2 = 1 - Math.Sqrt(2) * w + w * w;

            b0 = 1;
            b1 = -2;
            b2 = 1;
        }

            break;

        case EFilterPass.BandPass:
        {
            var w = cutoff;
            var d = w / 4;

            // определим полосу фильтра как [w * 3 / 4, w * 5 / 4]
            var w1 = Math.Max(w - d, CutoffFrequency.Min);
            var w2 = Math.Min(w + d, CutoffFrequency.Max);
            w1 = TransformFrequency(w1);
            w2 = TransformFrequency(w2);

            var w0Sqr = w2 * w1; // w0^2
            var wd = w2 - w1; // W

            a0 = -1 - wd - w0Sqr;
            a1 = 2 - 2 * w0Sqr;
            a2 = -1 + wd - w0Sqr;
            b0 = -wd;
            b1 = 0;
            b2 = wd;
        }

            break;

        default:
            throw new ArgumentOutOfRangeException();
    }

    _tablel.B0 = _tabler.B0 = b0 / a0;
    _tablel.B1 = _tabler.B1 = b1 / a0;
    _tablel.B2 = _tabler.B2 = b2 / a0;
    _tablel.A1 = _tabler.A1 = a1 / a0;
    _tablel.A2 = _tabler.A2 = a2 / a0;
}

Полный код класса ButterworthFilter

public enum EFilterPass
{
    None,

    LowPass,
    HiPass,
    BandPass
}

public class ButterworthFilter : SyntageAudioProcessorComponentWithParameters<AudioProcessor>, IProcessor
{
    private class BiquadConvolutionTable
    {
        public double B0, B1, B2, A1, A2;

        private readonly double[] _x = new double[3];
        private readonly double[] _y = new double[3];

        public double Process(double s)
        {
            // "сдвигаем" предыдущие семплы
            _x[2] = _x[1];
            _x[1] = _x[0];
            _x[0] = s;

            _y[2] = _y[1];
            _y[1] = _y[0];

            // свертка
            _y[0] = B0 * _x[0] + B1 * _x[1] + B2 * _x[2] - A1 * _y[1] - A2 * _y[2];

            return _y[0];
        }
    }

    private readonly BiquadConvolutionTable _tablel;
    private readonly BiquadConvolutionTable _tabler;

    public EnumParameter<EFilterPass> FilterType { get; private set; }
    public FrequencyParameter CutoffFrequency { get; private set; }

    public ButterworthFilter(AudioProcessor audioProcessor) : base(audioProcessor)
    {
        _tablel = new BiquadConvolutionTable();
        _tabler = new BiquadConvolutionTable();
    }

    public override IEnumerable<Parameter> CreateParameters(string parameterPrefix)
    {
        FilterType = new EnumParameter<EFilterPass>(parameterPrefix + "Pass", "Filter Type", "Filter", false);
        CutoffFrequency = new FrequencyParameter(parameterPrefix + "Cutoff", "Filter Cutoff Frequency", "Cutoff");

        return new List<Parameter> {FilterType, CutoffFrequency};
    }

    public void Process(IAudioStream stream)
    {
        if (FilterType.Value == EFilterPass.None)
            return;

        var count = Processor.CurrentStreamLenght;
        var lc = stream.Channels[0];
        var rc = stream.Channels[1];
        for (int i = 0; i < count; ++i)
        {
            var cutoff = CutoffFrequency.Value;
            CalculateCoefficients(cutoff);

            var ls = _tablel.Process(lc.Samples[i]);
            lc.Samples[i] = ls;

            var rs = _tabler.Process(rc.Samples[i]);
            rc.Samples[i] = rs;
        }
    }

    private double TransformFrequency(double w)
    {
        return Math.Tan(Math.PI * w / Processor.SampleRate);
    }

    private void CalculateCoefficients(double cutoff)
    {
        double b0, b1, b2, a0, a1, a2;

        switch (FilterType.Value)
        {
            case EFilterPass.LowPass:
            {
                var w = TransformFrequency(cutoff);

                a0 = 1 + Math.Sqrt(2) * w + w * w;
                a1 = -2 + 2 * w * w;
                a2 = 1 - Math.Sqrt(2) * w + w * w;

                b0 = w * w;
                b1 = 2 * w * w;
                b2 = w * w;
            }

                break;

            case EFilterPass.HiPass:
            {
                var w = TransformFrequency(cutoff);

                a0 = 1 + Math.Sqrt(2) * w + w * w;
                a1 = -2 + 2 * w * w;
                a2 = 1 - Math.Sqrt(2) * w + w * w;

                b0 = 1;
                b1 = -2;
                b2 = 1;
            }

                break;

            case EFilterPass.BandPass:
            {
                var w = cutoff;
                var d = w / 4;

                // определим полосу фильтра как [w * 3 / 4, w * 5 / 4]
                var w1 = Math.Max(w - d, CutoffFrequency.Min);
                var w2 = Math.Min(w + d, CutoffFrequency.Max);
                w1 = TransformFrequency(w1);
                w2 = TransformFrequency(w2);

                var w0Sqr = w2 * w1; // w0^2
                var wd = w2 - w1; // W

                a0 = -1 - wd - w0Sqr;
                a1 = 2 - 2 * w0Sqr;
                a2 = -1 + wd - w0Sqr;
                b0 = -wd;
                b1 = 0;
                b2 = wd;
            }

                break;

            default:
                throw new ArgumentOutOfRangeException();
        }

        _tablel.B0 = _tabler.B0 = b0 / a0;
        _tablel.B1 = _tabler.B1 = b1 / a0;
        _tablel.B2 = _tabler.B2 = b2 / a0;
        _tablel.A1 = _tabler.A1 = a1 / a0;
        _tablel.A2 = _tabler.A2 = a2 / a0;
    }
}

Полосовой эквалайзер в библиотеке NAudio

Для работы с звуком, звуковыми файлами различных форматов на .NET есть хорошая библиотека NAudio [2].

Она содержит пространство имен NAudio.Dsp с функционалом для фильтрации, свертки [24], гейта [31], огибающей [7], БПФ [32] и других интересных штук.

Рассмотрим класс Equalizer (из примеров, пространство имен NAudioWpfDemo.EqualizationDemo), позволяющий производить эквализацию сигнала. Класс реализует ISampleProvider, который в функции Read(float[] buffer, int offset, int count) обрабатывает (изменяет) массив семплов buffer.
Конструктор принимает массив структуры EqualizerBand, которые описывают "полосы" эквалайзера:

class EqualizerBand
{
    public float Frequency { get; set; }
    public float Gain { get; set; }
    public float Bandwidth { get; set; }
}

Здесь Frequency — центральная частота полосы с параметром Q (Bandwidth, добротность фильтра [33]), c усилением Gain dB.

Если заглянуть в реализацию, то каждой полосе EqualizerBand соответствует класс BiQuadFilter который используется как полосовой (Peaking) фильтр. Все фильтры изменяют сигнал используются последовательно.

Класс EqualizerBand является реализацией фильтра Баттервота, с большим выборов типов фильтров и параметрами. Если посмотреть реализацию, можно увидеть схожие формулы и коэффициенты.

Пример использования класса Equalizer вы найдете в проекте NAudioWpfDemo в классе EqualizationDemoViewModel.

Программы для рассчета фильтров

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

Программирование&Музыка: Частотный фильтр Баттервота. Часть 3 - 24

Iowa Hills Software RF Filter Designer

В следующей статье я расскажу про эффект delay [34], distortion [35] и модуляцию параметров.

Всем добра!
Удачи в программировании!

Список литературы

Не забывайте смотреть списки статей и книг в предыдущих статьях.

  1. Digital Signal Processing: A Practical Approach [22]. Русская версия — Цифровая обработка сигналов. Практический подход [23].
  2. Хабр-статья "Простыми словами о преобразовании Фурье" [20]
  3. Фильтр Баттерворта на вики [36]
  4. Github-репозитарий с кодом для рассчета фильтров Баттервота [37]
  5. DISCRETE SIGNALS AND SYSTEMS, Chapter 7. FIR and IIR Filters [38]
  6. How does a low-pass filter programmatically work? (dsp.stackexchange.com/) [39]
  7. Designing Digital Butterworth and Chebyshev Filters [40]
  8. Audio EQ Cookbook [41]
  9. Iowa Hills Software — Digital and Analog Filters [42]

Автор: lis355

Источник [43]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/200793

Ссылки в тексте:

[1] VST: https://ru.wikipedia.org/wiki/Virtual_Studio_Technology

[2] NAudio: https://naudio.codeplex.com/

[3] матана: https://habrastorage.org/files/29b/79e/889/29b79e8894a541e99e1e556ac3c5ebcf.jpg

[4] написанного мною синтезатора: https://www.youtube.com/watch?v=6zAVMEtIb2w

[5] GitHub'е: https://github.com/lis355/Syntage

[6] Понимаем и пишем VSTi синтезатор на C# WPF: https://habrahabr.ru/post/311220/

[7] ADSR-огибающая сигнала: https://habrahabr.ru/post/311750/

[8] Эквалайзер: #Part1

[9] Фильтрация через преобразование Фурье: #Part2

[10] Цифровые фильтры: #Part3

[11] Почему фильтр Баттервота?: #Part4

[12] Вывод формулы фильтра НЧ: #Part5

[13] Вывод формулы фильтра ВЧ и полосового фильтра: #Part6

[14] Программирование фильтра Баттервота по полученным формулам: #Part7

[15] Полосовой эквалайзер в библиотеке NAudio: #Part8

[16] Программы для рассчета фильтров: #Part9

[17] Список литературы: #Part10

[18] эквалайзер: http://ru.wikipedia.org/wiki/Эквалайзер

[19] смотрим википедию: http://ru.wikipedia.org/wiki/Фильтр_(электроника)

[20] дискретное преобразование Фурье: https://habrahabr.ru/post/196374/

[21] вычислительную сложность: http://ru.wikipedia.org/wiki/Вычислительная_сложность

[22] Digital Signal Processing: A Practical Approach: https://www.amazon.com/Digital-Signal-Processing-Practical-Approach/dp/0201596199

[23] Цифровая обработка сигналов. Практический подход: https://www.ozon.ru/context/detail/id/5689080/

[24] свертка: http://ru.wikipedia.org/wiki/Свёртка_(математический_анализ)

[25] импульсная характеристика фильтра: http://ru.wikipedia.org/wiki/Импульсная_переходная_функция

[26] передаточная функция фильтра: http://ru.wikipedia.org/wiki/Передаточная_функция

[27] АЧХ: http://ru.wikipedia.org/wiki/Амплитудно-частотная_характеристика

[28] s-плоскости: http://ru.wikipedia.org/wiki/S-плоскость

[29] z-плоскости: http://ru.wikipedia.org/wiki/Z-преобразование

[30] наибольший общий делитель (НОД, GCD): http://ru.wikipedia.org/wiki/Наибольший_общий_делитель

[31] гейта: http://wikisound.org/%D0%93%D0%B5%D0%B9%D1%82

[32] БПФ: http://ru.wikipedia.org/wiki/Быстрое_преобразование_Фурье

[33] добротность фильтра: http://ru.wikipedia.org/wiki/Добротность

[34] эффект delay: http://ru.wikipedia.org/wiki/Дилэй

[35] distortion: http://ru.wikipedia.org/wiki/Дисторшн

[36] Фильтр Баттерворта на вики: http://ru.wikipedia.org/wiki/Фильтр_Баттерворта

[37] Github-репозитарий с кодом для рассчета фильтров Баттервота: https://github.com/ruohoruotsi/Butterworth-Filter-Design

[38] DISCRETE SIGNALS AND SYSTEMS, Chapter 7. FIR and IIR Filters: http://abut.sdsu.edu/TE302/Chap7.pdf

[39] How does a low-pass filter programmatically work? (dsp.stackexchange.com/): http://dsp.stackexchange.com/questions/8693/how-does-a-low-pass-filter-programmatically-work

[40] Designing Digital Butterworth and Chebyshev Filters: http://mymbs.mbs.net/~pfisher/FOV2-0010016C/FOV2-0010016E/FOV2-001001A3/chapters/25katsianos/index.html

[41] Audio EQ Cookbook: http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt

[42] Iowa Hills Software — Digital and Analog Filters: http://www.iowahills.com/8DownloadPage.html

[43] Источник: https://habrahabr.ru/post/313062/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best