Создание аудиоплагинов, часть 5

  • Tutorial
Все посты серии:
Часть 1. Введение и настройка
Часть 2. Изучение кода
Часть 3. VST и AU
Часть 4. Цифровой дисторшн
Часть 5. Пресеты и GUI
Часть 6. Синтез сигналов
Часть 7. Получение MIDI сообщений
Часть 8. Виртуальная клавиатура
Часть 9. Огибающие
Часть 10. Доработка GUI
Часть 11. Фильтр
Часть 12. Низкочастотный осциллятор
Часть 13. Редизайн
Часть 14. Полифония 1
Часть 15. Полифония 2
Часть 16. Антиалиасинг



Давайте добавим несколько предустановок для плагина и создадим [относительно] симпатичный интерфейс.

Пресеты


Пресет — это настройки, позволяющие хранить конкретные значения параметров. На данный момент в нашем коде пресеты состоят из названия и значений для всех параметров, но, как мы увидим позже, тут можно хранить и другие данные. Пресеты еще иногда называют программами (programs).

Для начала давайте сделаем окно GUI пошире. Поменяйте соответствующую константу в resource.h:

// GUI default dimensions
#define GUI_WIDTH 400


В DigitalDistortion.h объявим private функцию-член класса:

private:
  double mThreshold;
  void CreatePresets();

А в DigitalDistortion.cpp добавим

void DigitalDistortion::CreatePresets() {
  MakePreset("clean", 100.0);
  MakePreset("slightly distorted", 80.0);
  MakePreset("woooo", 40.0);
  MakePreset("waaaa", 20.0);
  MakePreset("buzzz!!!", 0.01);
}


MakePreset в качестве аргументов берет имя пресета и значения для всех параметров. Порядок следования аргументов определен в enum EParams вверху файла.
Обратите внимание, что значения параметров меняются от 0 до 100, а не от 0 до 1. Эти значения относятся к параметру kThreshold, а не к переменной mThreshold.

Вместо вызова функции MakeDefaultPreset в конце конструктора напишите вызов CreatePresets:

DigitalDistortion::DigitalDistortion(IPlugInstanceInfo instanceInfo)
  : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), mThreshold(1.)
{
  // ...

  CreatePresets();
}


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

const int kNumPrograms = 5;


Если значение этой константы больше, чем реальное количество пресетов, остальные просто будут называться «Empty».

Запустите сборку VST2. Возможно придется удалить плагин с дорожки и подгрузить его повторно, чтобы появились свеженаписанные пресеты. Их можно менять, используя выпадающий список, который находится прямо над GUI плагина. Попробуйте разные — ручка поворачивается в соответствии со значением в пресете.
Напомню, что OnParamChange вызывается при изменении каждого параметра.
Справа от выпадающего меню есть кнопка, открывающая меню пресетов:



Попробуйте переименовать пресет и покрутить ручку. Ее положение не запомнится, если удалить плагин с дорожки и загрузить снова. Чтобы сохранить эти данные, щелкните “Export VST patch/bank file (.fxp/.fxb)…” и сохраните файл test.fxb на рабочий стол. Теперь удалите плагин, снова добавьте его и подгрузите этот файл с настройками.
Плагины VST для хранения отдельного пресета используют формат FXP, а для хранения банка пресетов — FXB.

Запустите таргет AU (возможно сначала придется зайти в Edit Scheme, чтобы он запускался вместе с хостом). REAPER по умолчанию пишет «No preset», но в выпадающем списке все пресеты на месте и все работает как надо. Сохранение пресетов в плагине работает иначе — мы не можем импортировать и экспортировать банки пресетов. Процессы сохранения\загрузки описаны Оли Ларкиным более детально в этом посте.

Пора, наконец, приступать к созданию красивого интерфейса!

GUI


Такого результата мы хотим добиться:



Это конечно не офигительно круто, но мы узнаем как можно добавлять графику и, что еще интереснее, как можно сделать вращающуюся ручку.
Скачайте этот TIFF с дизайном. Откройте в Фотошопе (или в чем вам больше нравится) и взгляните на разные слои и группы.
Как видим, единственное, что меняется — это ручка. Так что остальное можно просто сохранить в одном png:



Добавление фона для сборок на Mac


Сохраните картинку выше себе на рабочий стол. В Xcode в Project Navigator раскройте папку Resources и перетяните туда фоновую картинку:



Надо поставить галочку напротив Copy items into folder и добавить наше изображение для всех таргетов:



Добавление фона для сборок на Windows


В Visual Studio добавление ресурсов (типа графики) немного отличается. Добавление для всех таргетов на Маке означает, что картинка будет включена в файлы .app, .vst и .component, которые вы предоставляете своим пользователям. В Visual Studio для этого надо сначала кинуть нашу картинку в подпапку проекта resources\img, а затем отредактировать файл .rc в проекте. В данном случае, DigitalDistortion.rc. Его шапка выглядит так:

#include "resource.h"
KNOB_ID       PNG KNOB_FN


Сначала подключается resource.h, так что все #define из него доступны и в этом файле. Следующая строка объявляет, что будет добавлена ручка. Чтобы добавить фон, допишите это строку:

BACKGROUND_ID       PNG BACKGROUND_FN


Через минутку добавим BACKGROUND_ID и BACKGROUND_FN в resource.h.
Каждый раз при добавлении изображения к проекту нам нужно добавлять подобную строчку.

Фоновое изображение


Давайте подцепим нашу картинку в плагин. Где-то в районе 60-й строки в файле resource.h нужно изменить размер окна:

// GUI default dimensions
#define GUI_WIDTH 280
#define GUI_HEIGHT 230


и добавить уникальный ID и имя файла изображения:

// Unique IDs for each image resource.
#define KNOB_ID 101
#define BACKGROUND_ID 102

// Image resource locations for this plug.
#define KNOB_FN "resources/img/knob.png"
#define BACKGROUND_FN "resources/img/background.png"


В DigitalDistortion.cpp надо изменить конструктор, чтобы использовать наш файл вместо красного цвета:

// pGraphics->AttachPanelBackground(&COLOR_RED);
pGraphics->AttachBackground(BACKGROUND_ID, BACKGROUND_FN);


Это было несложно. Давайте соберем APP и полюбуемся на картинку:



Ручка


Ручка немного сложнее. У нее есть текстура и отражения, она состоит из нескольких слоев. Когда крутишь ручку, металл вращается, а лампа на потолке — нет (будем надеяться). Так что какие-то составляющие должны вращаться, а какие-то, имеющие отношение к освещению, не должны. Поэтому мы не сможем сохранить ручку как одну картинку и крутить ее в процессе.
Я сделал так: у ручки есть основа, которая не вращается; поверх нее металлическая текстура, которая вращается; и сверху всего этого — отражения, которые не вращаются. Все три части представлены по отдельности (тут я добавил черный фон, просто чтобы было видно границы и светлые части):



Для нашей задачи есть очень хороший инструмент: KnobMan. Это бесплатная программа, специально для создания ручек. Скачайте, запустите, и откройте в ней файл, который я подготовил:



Все, что я тут сделал — добавил три слоя и немного поменял некоторые координаты, чтобы металлическая текстура вращалась правильно. Если хотите разобраться с программой получше, прочитайте руководство.
Экспортируйте эту ручку (Cmd+E на Mac, Ctrl+E на Windows) и назовите файл knob.png (или скачайте мой). В этом png есть 128 кадров для разных положений ручки, и вращаются только металл со стрелкой. То, что надо.

В Xcode удалите существующий knob.png (правый клик, Delete). В появивщемся диалоге выберите Move to Trash. Теперь перетяните новый knob.png в папку img и проделайте все то же самое, что мы делали с фоновым изображением.
Так как имя файла не поменялось, нам нет необходимости менять resource.h. Переключайтесь сразу на DigitalDistortion.cpp изменяйте enum ELayout:

enum ELayout
{
  kWidth = GUI_WIDTH,
  kHeight = GUI_HEIGHT,

  kThresholdX = 79,
  kThresholdY = 62,
  kKnobFrames = 128
};


Как видно, нам даже не приходится указывать для WDL, что новая ручка больше. Нам нужно только указать количество кадров и задать координаты.
Запускайте VST2 и посмотрите, как при вращении ручки меняются кадры. Однако меня смущает то, что плагин называется дисторшн, а при ручке, выкрученной на 100%, у нас чистый звук.

Направление ручки



Где-то около 90-й строки надо внести изменения:

mThreshold = 1 - (GetParam(kThreshold)->Value() / 100.);


Чтобы предотвратить деление на ноль, о котором мы говорили ранее, надо изменить значение по умолчанию на 0, и максимальное значение на 99.99 (примерно 30-я строка):

GetParam(kThreshold)->InitDouble("Threshold", 0.0, 0.0, 99.99, 0.01, "%");


Теперь все должно работать как надо. С семантической точки зрения хорошо бы еще переименовать переменную Threshold во что-то типа DistortionAmount, так как единственное, что представляет пороговое значение, это переменная mThreshold.

Промежуточные результаты


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

Файлы со всеми результатами работы можно скачать.

Дополнительные материалы


Существует много бесплатных примеров для KnobMan тут и тут.

При создании GUI я пользовался вот этим уроком по Фотошопу. Спасибо автору!

И спасибо g200kg за KnobMan!

Оригиналы статей:
martin-finke.de/blog/articles/audio-plugins-006-presets
martin-finke.de/blog/articles/audio-plugins-007-gui
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 0

Only users with full accounts can post comments. Log in, please.