Разработка аудиоплагинов с помощью DPF: первый плагин


Что такое DPF?

DPF, сокращенное название Distrho Plugin Framework, это фреймворк для создания аудио плагинов на C++, по сравнению с JUCE он меньше и более “сырой”, но в нем нет коммерческого лицензирования или ограничений, также он поддерживает открытые форматы, такие как LADSPA и LV2. Он поддерживает Linux и Windows, здесь я предполагаю, что вы используете Linux, поэтому некоторые команды могут отличаться в Windows.

Настройка проекта DPF

Добавление DPF в ваш проект

Одна из основных трудностей, с которыми я столкнулся при работе с DPF, заключалась в том, чтобы понять, как добавить его в свой проект, точнее, в свой git-репозиторий, поэтому я остановлюсь на этом немного подробнее, специально для тех, кто не привык к этому.

Сначала вам нужно создать git-проект, вы можете просто создать его на GitHub или GitLab, или любом другом сайте git-хостинга, и клонировать его, или вы можете сделать это прямо на своем компьютере (не забудьте добавить удаленный доступ позже). Мы будем делать последнее:

mkdir -v DPFTutorial && cd DPFTutorial
git init
Войти в полноэкранный режим Выйти из полноэкранного режима

С помощью этих команд мы создали новую папку и инициализировали в ней git, теперь давайте добавим DPF в качестве подмодуля:

git submodule add https://github.com/DISTRHO/DPF dpf
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вы не знаете о подмодулях git, я рекомендую потратить время на чтение, это не сложно, но вкратце это означает, что вы добавляете git-проект внутри вашего git-проекта, где вы можете изменять коммиты, ветки и другие вещи отдельно.

Создание папки plugins

Теперь мы создадим папку plugins, которая будет содержать наш плагин(ы):

mkdir -v plugins
Вход в полноэкранный режим Выйти из полноэкранного режима

Это структура папок по умолчанию, которую вы найдете в проектах DPF, я думаю, она довольно проста.

projectdir/
└── dpf/ (git submodule)
└── plugins/
    └── MyPlugin (not created yet)
Вход в полноэкранный режим Выход из полноэкранного режима

Создание плагина

Поскольку мы теперь знаем, как установить правильное окружение, давайте начнем с нашего плагина, это будет самый простой плагин, который вы можете создать, усилитель, который вы можете увеличивать или уменьшать усиление, поскольку цель здесь – как использовать DPF, я не буду показывать, как создавать сложные плагины.

Давайте создадим папку для плагина:

mkdir -v MyAmp
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь у нас есть следующая структура:

DPFTutorial/
└── dpf/ (git submodule)
└── plugins/
    └── MyAmp/
Вход в полноэкранный режим Выход из полноэкранного режима

Установка информации о плагине

Теперь мы создадим файл DistrhoPluginInfo.h в нашей папке plugin, в этом файле находятся некоторые данные о плагине, такие как название, количество входов и выходов, принимает ли он midi и т.д.. Эта информация задается макросами, подробнее о них вы можете узнать здесь.

#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED

#define DISTRHO_PLUGIN_NAME  "MyAmp"
#define DISTRHO_PLUGIN_URI   "https://github.com/REIS0/DPFTutorial"

#define DISTRHO_PLUGIN_NUM_INPUTS   1
#define DISTRHO_PLUGIN_NUM_OUTPUTS  1
#define DISTRHO_PLUGIN_IS_RT_SAFE   1

#endif
Вход в полноэкранный режим Выйти из полноэкранного режима

Этим мы сообщили DPF информацию о нашем плагине DISTRHO_PLUGIN_NAME – название нашего плагина, DISTRHO_PLUGIN_URI – URI нашего плагина (я не буду подробно описывать, что такое URI, но вы можете почитать), вы можете просто использовать URL вашего git-репозитория, DISTRHO_PLUGIN_NUM_INPUTS задает количество входов нашего плагина, DISTHRO_PLUGIN_NUM_OUTPUTS задает количество выходов, и DISTHRO_PLUGIN_IS_RT_SAFE говорит, безопасно ли использовать плагин в контексте реального времени, так как наш плагин очень простой, мы установили это значение 1.

Добавление параметров

Теперь мы зададим параметры, которые будут у нашего плагина, пока это только декларации, позже мы зададим больше информации, например, значения, их название в GUI и т.д. Добавьте их в DistrhoPluginInfo.h, после информации о плагине и непосредственно перед секцией #endif:

...

enum Parameters {
    kGain,
    kParameterCount
};

#endif
Вход в полноэкранный режим Выйти из полноэкранного режима

Параметры представляют собой одну структуру enum, kGain – наш единственный параметр, kParameterCount – количество параметров (для тех, кто не знает, как работает enum, в основном каждый элемент соответствует числу, поэтому kGain будет 0, а kParameterCount будет 1, обратите внимание, что 1 – это количество параметров), это будет важно позже, когда мы создадим наш файл плагина.

Сам плагин

Теперь давайте создадим наш плагин, сначала создадим файл MyAmp.cpp в той же папке, затем добавим код:

#include "DistrhoPlugin.hpp"

START_NAMESPACE_DISTRHO

class MyAmp : public Plugin {
    public:
        MyAmp() : Plugin(kParameterCount, 0, 0), gain(1.0) {}

    private:
        float gain;

        DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MyAmp);
}

Plugin *createPlugin() { return new MyAmp(); }

END_NAMESPACE_DISTRHO
Вход в полноэкранный режим Выход из полноэкранного режима

В первой строке мы включаем DistrhoPlugin.hpp для импорта функций, которые нам понадобятся, во второй и в конце START_NAMESPACE_DISTRHO и END_NAMESPACE_DISTRHO, как следует из названия, это устанавливает пространство имен для функций, если вы не знаете о пространствах имен, в интернете есть тонна ресурсов, чтобы прочитать об этом, а пока просто помните, что нам нужно добавить их в наш код. После этого мы создаем наш класс MyAmp, как вы можете видеть, это имя нашего плагина, не обязательно иметь такое же имя, но это хорошая практика, поэтому я настоятельно рекомендую использовать его, затем нам нужно наследоваться от класса Plugin, этим мы устанавливаем, что наш класс будет классом типа плагина, и мы добавляем конструктор для Plugin с параметрами: количество параметров в плагине, количество программ в плагине и количество состояний в плагине, для этого плагина мы установим только количество параметров, используя kParameterCount, который мы объясняли ранее. Наконец, мы установим начальное значение для переменной gain, которая будет считывать и хранить значения для параметра kGain (поэтому с такой логикой мы можем легко предположить, что для каждого параметра нам понадобится переменная в классе нашего плагина).

Добавление некоторой информации о “метаданных”

Теперь добавим немного информации о нашем плагине, эта информация будет больше похожа на метаданные, автор, лицензирование и т.д:

    // PUBLIC SPACE
...
    protected:

        const char *getLabel() const override { return "MyAmp"; }
        const char *getDescription() const override {
            return "Simple amp plugin.";
        }
        const char *getMaker() const override { return "REIS0"; }
        const char *getLicense() const override { return "MIT"; }
        uint32_t getVersion() const override { return d_version(1,0,0); }
        int64_t getUniqueId() const override { 
            return d_const('M','A','D','T'); 
        }

    private:
...
Вход в полноэкранный режим Выйти из полноэкранного режима

Функции говорят сами за себя, но мы быстро рассмотрим их: getLabel() предназначена для ярлыка плагина, почти всегда вы будете ставить название плагина, getDescription() для описания плагина, getMaker() кто создал плагин, здесь мое имя пользователя, но вы должны установить свое собственное, getLicense() для лицензии, я использую MIT здесь, но вы можете выбрать любую, которую вы предпочитаете, getVersion() для версии плагина, так что если это 1. 0.0 мы установим как d_version(1,0,0), getUniqueId() устанавливает ID плагина, это используется хостом, чтобы узнать ваш плагин и не вызывать конфликтов между другими плагинами, здесь я установил 'M','A','D','T', который я взял из My Amp DPF Tutorial, лично я предпочитаю устанавливать его связанным с именем плагина, но это ваше дело.

Параметры

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

// SOME METADATA
...
    void initParameter (uint32_t index, Parameter& parameter) override {
        switch (index) {
            case kGain:
                parameter.name = "Gain";
                parameter.symbol = "gain";
                parameter.ranges.def = 1.0f;
                parameter.ranges.min = 0.0f;
                parameter.ranges.max = 2.0f;
                break;
            default:
                break;
        }
    }
...
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь мы используем switch для согласования параметров, если бы у нас было более одного параметра, нам бы пришлось добавить больше случаев, так как у нас только kGain в этом нет необходимости. Давайте пройдем через эти случаи: parameter.name задает имя параметра, поэтому когда вы откроете плагин, вы увидите на нем “Gain”, затем у нас есть parameter.symbol, мы можем сравнить его с id параметра, поэтому он должен быть уникальным, и вы не можете повторять его для других параметров, parameter. ranges устанавливает значения для нашего параметра, def – это значение по умолчанию, когда мы открываем плагин в первый раз, min – минимально возможное значение, max – максимально возможное значение.

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

...
    float getParameterValue(uint32_t index) const override {
        switch (index) {
        case kGain:
            return gain;
        default:
            return 0.0;
        }
    }

    void setParameterValue(uint32_t index, float value) override {
        switch (index) {
        case kGain:
            gain = value;
            break;
        default:
            break;
        }
    }
...
Вход в полноэкранный режим Выход из полноэкранного режима

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

Теперь, когда у нас все настроено, мы можем наконец написать логику обработки, так как мы работаем с DPF установкой, я не буду углубляться в часть обработки звука, также это просто усилитель, поэтому все будет очень просто и не нужно много объяснять:

...
    void run(const float **inputs, float **outputs, uint32_t frames) override {
        const float *const in = inputs[0];
        float *const out = outputs[0];

        for (uint32_t i = 0; i < frames; i++) {
            out[i] = in[i] * gain;
        }
    }

private:
...
Вход в полноэкранный режим Выход из полноэкранного режима

Сначала мы назначим входы и выходы, поскольку у нас только один вход и один выход, как мы установили в DistrhoPluginInfo.h, мы просто получим позицию 0, но если бы у нас было две, нам нужно было бы отобразить позиции 0 и 1, и так далее по мере увеличения количества входов и выходов. Теперь мы пройдем через наши frames, также известные как сэмплы (если вы не знаете, что я имею в виду, посмотрите здесь), и сделаем обработку, поскольку это просто усилитель, эта часть очень проста.

Makefiles

Теперь, когда у нас есть класс нашего плагина, нам нужно его собрать (недавно в DPF была добавлена поддержка cmake, но так как я не знаю, как использовать cmake вообще, здесь это не будет рассматриваться), для этого мы напишем два Makefiles, если вы не знаете, что такое Makefiles, вы можете просто поискать и найти несколько хороших объяснений и руководств, первый Makefile будет в папке нашего плагина:

DPFTutorial/
└── dpf/ (git submodule)
└── plugins/
    └── MyAmp/
        └── DistrhoPluginInfo.h
        └── MyAmp.cpp
        └── Makefile
Вход в полноэкранный режим Выход из полноэкранного режима
#!/usr/bin/make -f
# Makefile for DISTRHO Plugins #
# ---------------------------- #
# Created by falkTX
#
# Modified by REIS0

VST2 ?= true
LV2 ?= true

# --------------------------------------------------------------
# Project name, used for binaries

NAME = MyAmp

# --------------------------------------------------------------
# Files to build

FILES_DSP = MyAmp.cpp

# --------------------------------------------------------------
# Do some magic

include ../../dpf/Makefile.plugins.mk

# --------------------------------------------------------------
# VST2 and LV2 targets

ifeq ($(VST2), true)
TARGETS += vst
endif

ifeq ($(LV2), true)
ifeq ($(HAVE_DGL),true)
TARGETS += lv2_sep
else
TARGETS += lv2_dsp
endif
endif

all: $(TARGETS)

# --------------------------------------------------------------

Войти в полноэкранный режим Выход из полноэкранного режима

Если вы не привыкли к Make-файлам, они могут показаться немного сложными, но не волнуйтесь, вы можете просто скопировать этот файл и изменить его в соответствии с вашими потребностями. Итак, вот что мы в нем делаем: сначала мы устанавливаем, в какие форматы плагина мы будем компилировать его, в данном случае LV2 и VST (в DPF также есть опция как автономное приложение JACK), затем мы устанавливаем имя нашего плагина, файлы, необходимые для компиляции, включаем DPF, а затем некоторые инструкции для компиляции.

Второй Makefile мы поместим в корневую папку нашего проекта:

DPFTutorial/
└── dpf/ (git submodule)
└── plugins/
    └── MyAmp/
└── Makefile
Войти в полноэкранный режим Выйти из полноэкранного режима
#!/usr/bin/make -f
# Makefile for DISTRHO Plugins #
# ---------------------------- #
# Created by falkTX
#
# Modified by REIS0

PLUGIN=MyAmp

include dpf/Makefile.base.mk

all: dgl plugins gen

# --------------------------------------------------------------

dgl:
ifeq ($(HAVE_OPENGL),true)
 $(MAKE) -C dpf/dgl opengl
endif

plugins: dgl
 $(MAKE) all -C plugins/$(PLUGIN)

ifneq ($(CROSS_COMPILING),true)
gen: plugins dpf/utils/lv2_ttl_generator
 @$(CURDIR)/dpf/utils/generate-ttl.sh
ifeq ($(MACOS),true)
 @$(CURDIR)/dpf/utils/generate-vst-bundles.sh
endif

dpf/utils/lv2_ttl_generator:
 $(MAKE) -C dpf/utils/lv2-ttl-generator
else
gen:
endif

# --------------------------------------------------------------

clean:
 $(MAKE) clean -C dpf/dgl
 $(MAKE) clean -C dpf/utils/lv2-ttl-generator
 $(MAKE) clean -C plugins/$(PLUGIN)
 rm -rf bin build

# --------------------------------------------------------------

.PHONY: plugins

Войти в полноэкранный режим Выйти из полноэкранного режима

В этом файле единственное, что нам нужно изменить, это переменную PLUGIN, все остальное вы можете просто оставить там, или отредактировать, если вы знаете, что делаете.

Компиляция плагина

Когда все готово, мы можем наконец скомпилировать и протестировать наш плагин, просто перейдите в корневую папку проекта и запустите make:

DPFTutorial/plugins/MyAmp$: make
Вход в полноэкранный режим Выйти из полноэкранного режима

Если все прошло хорошо, то должна быть создана папка bin с LV2 и VST плагином внутри, LV2 – это папка, а VST – это просто файл. Теперь просто загрузите его в DAW или хост, например Carla, и проверьте, работает ли он.

Заключение

На этом мы закончили первое руководство по DPF, хотя это было в основном руководство по настройке, которая, на мой взгляд, является самой трудной частью, когда кто-то впервые сталкивается с фреймворком DPF, я надеюсь, что я хоть чем-то помог. Возможно, в будущем я попытаюсь сделать руководство, ориентированное на пользовательский интерфейс. Вы можете найти весь проект в репозитории GitHub.

Оцените статью
Procodings.ru
Добавить комментарий