Створення аудіоплагіна, частина 2

    Всі пости серії:
 Частина 1. Введення та налаштування
 Частина 2. Вивчення коду
______________________________________________________________
 
 Вивчення коду
 
Давайте трохи краще розглянемо наш тестовий проект. Найважливіші файли — 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 .
 
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.