Создание аудиоплагинов, часть 2
Предыдущие посты из серии: Часть 1. Введение и настройка______________________________________________________________Изучение кодаДавайте получше рассмотрим наш тестовый проект. Самые важные файлы — resource.h, MyFirstPlugin.h и MyFirstPlugin.cpp. На данный момент плагин представляет собой простой регулятор громкости звука.
Константы, флаги и источники изображений Откройте в навигаторе resource.h. Этот файл содержит такие константы, как название плагина, его версию, уникальный ID и ссылки на источники изображений для GUI. В строках 23–26 можно прописать уникальный ID:
// 4 chars, single quotes. At least one capital letter #define PLUG_UNIQUE_ID 'Ipef' // make sure this is not the same as BUNDLE_MFR #define PLUG_MFR_ID 'Acme' ID важен для общей каталогизации плагинов. Можете зарегистрировать его здесь. Строки 56 и далее определяют ID и путь к изображению для той ручки громкости, которую вы видите при запуске плагина:
// Unique IDs for each image resource. #define KNOB_ID 101
// Image resource locations for this plug. #define KNOB_FN «resources/img/knob.png» Найдите в навигаторе и откройте Resources → img → knob.png. Это спрайт с шестьюдесятью различными положениями ручки, каждое размером 48 на 48 пикселей. Когда вы запускаете плагин и крутите ручку, изображение ручки не вращается. Программа только показывает соответствующую часть этого спрайта.Почему оно не вращается? Представьте себе, что ручка имеет какие-нибудь насечки и отбрасывает тень. Тогда при вращении тень тоже будет крутиться. Такого в действительности не бывает, как вы (надеюсь) знаете.
Ниже в resource.h можно установить размеры окна плагина:
#define GUI_WIDTH 300 #define GUI_HEIGHT 300 Поиграйтесь со значениями и запустите плагин.
Интерфейс класса плагина Откройте MyFirstPlugin.h. В нем содержится интерфейс для класса плагина. Часть public включает конструктор, деструктор и три функции-члена класса:
Reset вызывается, когда изменяется частота дискретизации. OnParamChange вызывается при изменении параметров плагина, например, когда мы крутим ручку. ProcessDoubleReplacing это самое ядро плагина. Именно в ней осуществляется обработка входящего аудио. В части private находится только переменная типа double, хранящая текущее значение громкости.
Реализация Переходим к интересной части! Откройте MyFirstPlugin.cpp. Сразу видим интересный приемчик с типом enum:
enum EParams { kGain = 0, kNumParams }; Устанавливая kGain = 0 и ставя kNumParams после него, kNumParams становится количеством параметров плагина (в данном случае 1).Следующий enum использует константы, описанные в resource.h и устанавливает координаты ручки в окне плагина:
enum ELayout { kWidth = GUI_WIDTH, kHeight = GUI_HEIGHT,
kGainX = 100, kGainY = 100, kKnobFrames = 60 }; Он также определяет количества кадров в knob.png равным 60.Далее начинается имплементация конструктора. Устанавливаются атрибуты плагина:
//arguments are: name, defaultVal, minVal, maxVal, step, label GetParam (kGain)→InitDouble («Gain», 50., 0., 100.0, 0.01,»%»); В GUI пока что не видно ни значения, ни значка процента, но значение может меняться от 0 до 100. Значение по умолчанию равно 50. Можно заметить, что градация значений не равномерна на окружности. Это из-за SetShape (2.). Если заменить это на SetShape (1.), то распределение значений будет линейным. Именно SetShape задает нелинейное поведение.Далее конструктор создает графический контекст нужного размена и задает фоновый красный цвет:
IGraphics* pGraphics = MakeGraphics (this, kWidth, kHeight); pGraphics→AttachPanelBackground (&COLOR_RED); После этого загружается knob.png, создается новый IKnobMultiControl с изображением и привязывается к GUI. IKnobMultiControl — это класс для ручек интерфейса.Обратите внимание, как передается параметр kKnobFrames для обозначения количества кадров в спрайте:
IBitmap knob = pGraphics→LoadIBitmap (KNOB_ID, KNOB_FN, kKnobFrames); pGraphics→AttachControl (new IKnobMultiControl (this, kGainX, kGainY, kGain, &knob)); Наконец, конструктор привязывает графический контекст и создает для плагина пресет по умолчанию:
MakeDefaultPreset ((char *) »-», kNumPrograms); Взглянем на OnParamChange (в конце файла). IMutexLock обеспечивает потоковую безопасность — концепт, который мы разберем попозже. Все остальное — это просто набор вариантов действий в зависимости от того, какой параметр изменяется:
case kGain: mGain = GetParam (kGain)→Value () / 100.; break; Как мы помним, kGain изменяется от 0 до 100. Так что после деления значения на 100 мы назначаем величину от 0 до 1 private члену класса mGain.
Итак, мы немного разобрали процесс создания GUI и привязку к нему таких параметров, как mGain. Давайте теперь взглянем на то, как плагин обрабатывает входящее аудио. В нашем случае аудио поток — это последовательность семплов, представленная типом данных double, каждый из которых содержит значение амплитуды сигнала в заданный момент времени.Первый параметр, передаваемый функции ProcessDoubleReplacing, это double** inputs. Последовательность значений типа double можно передать, используя double*. Но плагин обрабатывает два канала (стерео) или даже больше, так что нам нужны несколько последовательностей семплов, и мы сообщаем это при помощи double**. Первые две строки в функции иллюстрируют это:
double* in1 = inputs[0]; double* in2 = inputs[1]; in1 указывает на первую последовательность семплов (левый канал), in2 — на семплы правого канала. После выполнения аналогичных действий для выходного буфера мы можем итерировать над элементами входного и выходного буферов:
for (int s = 0; s < nFrames; ++s, ++in1, ++in2, ++out1, ++out2) { *out1 = *in1 * mGain; *out2 = *in2 * mGain; } Для каждого семпла мы берем входное значение, умножаем его на mGain и записываем его в выходной буфер. nFrames сообщает нам, сколько семплов на канал имеется, чтобы мы знали длину буферов.Вы могли заметить, что когда запускаете плагин как самостоятельное приложение, можно слышать себя из динамиков, если в компьютере есть встроенный микрофон. Это из-за того, что по умолчанию приложение использует этот микрофон как источник входного сигнала. Чтобы изменить это (и кое-что еще), зайдите в preferences:
Установите брэйкпоинт в функции Reset. Измените Sampling Rate справа и примените изменения. Отладчик прервет исполнение кода в функции Reset. Внизу справа, где работает lldb, Введите print GetSampleRate ().
Обратите внимание, что так же можно вызвать любую функцию из отладчика и посмотреть правильные значения. Нажмите Stop вверху слева, когда налюбуетесь и решите продолжить.Теперь пора создать из кода плагин и загрузить его в хост. Это и будет темой следующего поста.А пока что
Дополнительное чтение Чтобы заполнить некоторые пробелы, настоятельно рекомендую прочесть эти слайды авторства изобретательного г-на Оли Ларкина. В них найдутся некоторые из ключевых разъяснений о WDL-OL.