Статьи:

Профиль:

СДК — создание ВСТ миди эффекта


В данной публикации мы продолжаем знакомство с СДК библиотекой для создания ВСТ плагинов. Напомню то что данная библиотека ориентирована на работу с миди данными а не с аудио, называется «VST Module Architecture«, первая часть руководства пользователя была переведена ранее и содержит теоретическую базу.  Тут же более детально описывается непосредственно создание миди  ВСТ эффекта, классы языка С++ для работы с миди данными.

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

Введение

Данная секция описывает итерфейс программирования миди эффектов (API) компании Стеинберг. Миди эффекты могут быть использованы для обработки и генерирования миди данных в реальном времени. Типичным миди эффектом являются арпеджиатор и эхо. Итерфейс программирования миди эффектов полностью основывается на модульной ахитектуре ВСТ, описанной ранее. Все интерфейсы, которые используются в миди эффектах, могут быть найдены в файле imidieffect.h.

Интерфейсы со стороны плагина:

IMidiEffect Миди эффект
IGenericPlugController Параметр интерфейса
IUIFactory Редактор миди эффекта (под редактором понимается GUI, внешний вид плагина)
IPersistentChunk Хранение установок (пресетов)

СДК библиотека также работает с классом CMidiEffect, который обычно поддерживается всеми интерфейсами.

Интерфейсы со стороны хост-программы:

IMEAccessor Создаёт и управляет миди событиями, получает контекстную информацию.
IMasterTrackInfo
ITransportInfo
Получает более детальную контекстную информацию (изменение темпа и др.)
IHostApplication Стандартный интерфейс хост-программы

Ниже представлена схема взаимодействия интерфейсов библиотеки:

Миди эффекты работают прямо в структуре миди данных хост-программе. С точки зрения плагинов — различные миди события являются разновидностями IMEObjectID, который в С++ является простым указателем. Для создания и управления событием Вы должны передать данное событие в методы интерфейса IMEAccessor. Основным правилом работы в реальном времени с миди событиями является необходимость собственноручно уничтожать/дезактивировать события, которые были ранее созданы вами же (смотри IMEAccessor::destroyEvent). Работа с временем и все временные значения заданы в PPQ — на русский язык расшифровывается как колличество «ударов в четверть ноты». Работа с темпом осуществляется с помощью методов IMasterTrackInfo::getPpqBase и IMasterTrackInfo::ppq2seconds (которые будут рассмотрены несколько более позже).

Создание миди эффекта

Итак начнём, для того чтобы создать новый миди эффект с помощью языка С++ Вам необходимо следующее:

1) Создать новый проект динамической .dll библиотеки

2) Добавить следующие файлы из СДК в Ваш проект:

..\pluginterfaces\funknown.cpp Базовый интерфейс FUnknown
source\main\dllmain.cpp для работы .длл библиотеки
source\common\pluginfactory.cpp описание CPluginFactory
source\common\linkedlist.cpp необходимо для CPlugParams и CMidiEventQueue
source\common\plugparams.cpp описание CPlugParams
source\common\plugxmlgui.cpp Загрущик ресурсов
source\common\plugxmlgui.cpp описание CMidiEffect
source\midi\eventqueue.cpp описание CMidiEventQueue

3) Не забудьте задать дополнительные добавочные папки в настройках проекта (Project — Settings — C/C++ — Preprocessor) т.е. »../../pluginterfaces,../source/common,../source/midi»
4) Опишите ваш класс эффекта из CMidiEffect (можно копировать из  примера реализации эффекта миди -эхо, данного в СДК библиотеке)

5) Добавьте входную точку плагина в GetPluginFactory (см. первую часть — основы плагина).

6) Добавьте DEF файл в Ваш проект для экспорта  GetPluginFactory.

Типы данных интерфейса программирования миди

MidiStatus

Подключаемый файл: imidieffect.h

enum MidiStatus
{
	kNoteOff = 0x80,
	kNoteOn = 0x90,
	kPolyPressure = 0xA0,
	kController = 0xB0,
	kProgramChange = 0xC0,
	kAfterTouch = 0xD0,
	kPitchBend = 0xE0,

	kSysexStart = 0xF0,
	kQuarterFrame = 0xF1,
	kSongPointer = 0xF2,
	kSongSelect = 0xF3,
	kCableSelect = 0xF5,
	kSysexEnd = 0xF7,
	kMidiClock = 0xF8,
	kMidiClockStart = 0xFA,
	kMidiClockContinue = 0xFB,
	kMidiClockStop = 0xFC,
	kActiveSensing = 0xFE,
	kMidiReset = 0xFF
};

Декларация поддерживаемых типов состояний миди, в соответствии с стандартом миди сообщений.

MidiEffectCaps

Подключаемый файл: imidieffect.h

struct MidiEffectCaps
{
size_t structSize; // sizeof(MidiEffectCaps), задаётся хост-программой
long flags; // смотри метод IMidiEffect::getCaps
};

Данная структура описывает возможности миди эффекта когда хост-программа запрашивает IMidiEffect::getCaps.

MidiContextInfo

Подключаемый файл: imidieffect.h

struct MidiContextInfo
{
	long tempo;
	long ppqPosition;
	long stopPosition;

	long performedCyclesPpq;
	long performedCyclesImmediatePpq;
	long numberOfPerformedCycles;

	long barPos;
	long barCount;
	long beatCount;
	long remainder;

	short numerator;
	short denominator;
};

Стуктура с метрической информацией о PPQ позиции в текущей секвенции хост-программы. Для заполнения данной структуры Вы должны вызвать метод IMEAccessor::getContextInfo.

tempo Темп в BPM (ударах в минуту)
ppqPosition Текущая PPQ позиция воспроизведения плагина, не совпадает с текущей позицией каретки воспроизведения проекта.
stopPosition В случае работы в режиме остановки, это ещё одна позиция старта. Все последующие запросы к методу IMidiEffect::playAction с параметром «правда» будут базироваться на этой позиции, как на позиции старта.
performedCyclesPpq Смотри performedCyclesImmediatePpq
performedCyclesImmediatePpq Счётчик колличества циклов в PPQ базе/системе исчисления.
numberOfPerformedCycles Удобно — если метод swapAction не поддерживает перезапись.
barPos Позиция старта текущего такта (первая сильная доля), основывающаяся на PPQ системе исчисления.
barCount ноле-базирующееся число тактов на PPQ позиции
beatCount ноле-базирующееся число ударов на PPQ позиции
remainder Остальные доли/удары
numerator числитель счётчика времени на PPQ позиции
denominator знаменатель счётчика времени на PPQ позиции

IMidiEffect

Наследственность: FUnknown <- IPluginBase <- IMidiEffect

Подключаемый файл: imidieffect.h

Данный интерфейс описывает миди эффект.

Методы:

  • IMidiEffect::getCaps
  • IMidiEffect::configure
  • IMidiEffect::receiveEvent
  • IMidiEffect::playAction
  • IMidiEffect::positAction
  • IMidiEffect::swapAction
  • IMidiEffect::stopAction
  • IMidiEffect::startAction

IMidiEffect::getCaps

tresult PLUGIN_API getCaps (MidiEffectCaps* c)

Заполните структуру MidiEffectCaps возможностями эффекта.

Флаги возможностей:

kMEIsEditable Плагин имеет редактор (редактором называется GUI) — запрашивает IUIFactory
kMEHasInspector Плагин имеет, не знаю что это «inspector view», видимо режим отладки плагина, также запрашивается интерфейс IUIFactory

IMidiEffect::configure

tresult PLUGIN_API configure (long usage)

Конфигурирует МИДИ эффект для использования в определённом режиме:

kMEUsageInsert Плагин используется в последовательном режиме (insert режим — плагин помещается на определённую дорожку микшера и обрабатывает только её)
kMEUsageSend Плагин используется в параллельном режиме (send режим — плагин может обрабатывать одновременно сразу несколько миди дорожек)
kMEUsageOffline Плагин используется для обработки миди сообщений в удалённом режиме (не в реальном времени).

IMidiEffect::receiveEvent

tresult PLUGIN_API receiveEvent (IMEObjectID event)

Получает только одно МИДИ событие от хост-программы. Вы можете задать тип события с помощью метода  IMEAccessor::isImmediateEvent.

IMidiEffect::playAction

tresult PLUGIN_API playAction (long fromPPQ, long toPPQ, bool immediate)

Данный эффект преобразует из fromPPQ to в PPQ.

IMidiEffect::positAction

tresult PLUGIN_API positAction (long position)

Движок секвенсора определяет новую позицию PPQ.

IMidiEffect::swapAction

tresult PLUGIN_API swapAction (long position, long length)

Движок секвенсора переходит к другой PPQ позиции без перезагрузки (циклический режим). Параметр length — это размер цикла в PPQ.

IMidiEffect::stopAction

tresult PLUGIN_API stopAction (long position)

Движок секвенсора остановлен на заданной PPQ позиции.

IMidiEffect::startAction

tresult PLUGIN_API startAction (long position)

Движок секвенсора начал воспроизведение с заданной PPQ позиции.

IMEAccessor

Наследственность: FUnknown <- IMEAccessor

Подключаемый файл: imidieffect.h

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

Создание событий и выход (output):

  • IMEAccessor::createMidiEvent
  • IMEAccessor::createSysexEvent
  • IMEAccessor::duplicateMidiEvent
  • IMEAccessor::destroyEvent
  • IMEAccessor::passToOutputQueue
  • IMEAccessor::passToOutput

Манипулирование событиями:

  • IMEAccessor::setStart
  • IMEAccessor::getStart
  • IMEAccessor::setLength
  • IMEAccessor::getLength
  • IMEAccessor::setImmediateEvent
  • IMEAccessor::isImmediateEvent
  • IMEAccessor::setChannel
  • IMEAccessor::getChannel
  • IMEAccessor::getStatus
  • IMEAccessor::setStatus
  • IMEAccessor::getData1
  • IMEAccessor::setData1
  • IMEAccessor::getData2
  • IMEAccessor::setData2
  • IMEAccessor::getData3
  • IMEAccessor::setData3
  • IMEAccessor::getMicroTuning
  • IMEAccessor::setMicroTuning

Контекстная информация:

  • IMEAccessor::getMidiChannel
  • IMEAccessor::getInterval
  • IMEAccessor::setInterval
  • IMEAccessor::getContextInfo
  • IMEAccessor::setCanPlayInStop

IMEAccessor::createMidiEvent

IMEObjectID PLUGIN_API createMidiEvent (
		MidiStatus type,
		long data1,
		long data2,
		long length)

Создаёт новое МИДИ событие с определённым статусом (описано ранее) и данными о длинне length (в PPQ). В случае успеха возвращается идентификатор нового события, иначе — НОЛЬ.

Заметка: Все события, созданные внутри плагина должны быть уничтожены/удалены с помощью метода IMEAccessor::destroyEvent.

Вы можете задать определённый МИДИ канал для этого эффекта с помощью метода  IMEAccessor::getMidiChannel. Если плагин работает в режиме посыла (send, режимы описаны ранее) и канал хост-программы не задан, — то  события плагина корректируются автоматически.

IMEAccessor* accessor; 

char status = (char)kNoteOn;
long channel = accessor->getMidiChannel ();
if(channel > 0)
	status |= (channel & 0xF);

IMEObjectID event = accessor->createMidiEvent ((MidiStatus)status, ... )

IMEAccessor::createSysexEvent

IMEObjectID PLUGIN_API createSysexEvent (unsigned char* data, long size)

Создаёт новое SysEx событие с заданными данными и размером. В случае успеха возвращает значение, соответствующее новому событию, иначе возвращает НОЛЬ. Заметка: данное событие должно быть уничтожено/удалено с помощью метода IMEAccessor::destroyEvent.

IMEAccessor::duplicateMidiEvent

IMEObjectID PLUGIN_API duplicateMidiEvent (IMEObjectID event)

Создаёт копию заданного миди события. Заметка: данное событие должно быть уничтожено/удалено с помощью метода IMEAccessor::destroyEvent.

IMEAccessor::destroyEvent

tresult PLUGIN_API destroyEvent (IMEObjectID event)

Уничтожает/удаляет заданное событие.

IMEAccessor::passToOutputQueue

tresult PLUGIN_API passToOutputQueue (IMEObjectID event)

Передаёт заданное событие на выход плагина (в хост-программе).

IMEAccessor::passToOutput

tresult PLUGIN_API passToOutput (IMEObjectID event)

Пеедаёт заданное событие прямо на выход эффекта, без ожидания в очереди событий.

Начало и конец события

tresult PLUGIN_API setStart (IMEObjectID event, long ppq)
long PLUGIN_API getStart (IMEObjectID event)
tresult PLUGIN_API setLength (IMEObjectID event, long ppq)
long PLUGIN_API getLength (IMEObjectID event)

Данные методы используются для задания или расчёта длинны миди события (нажатие клавиши) в PPQ системе исчисления.

IMEAccessor::setStart Задаёт позицию начала миди события
IMEAccessor::getStart Возвращает позицию начала миди события
IMEAccessor::setLength Задаёт длинну миди события
IMEAccessor::getLength Возвращает длинну миди события

Срочное событие

tresult PLUGIN_API setImmediateEvent (IMEObjectID event, bool state)
bool PLUGIN_API isImmediateEvent (IMEObjectID event)

Данные методы используются для задания «срочного (без ожидания)» состояния МИДИ события.

IMEAccessor::setImmediateEvent Задание событию тип «срочный»
IMEAccessor::isImmediateEvent Определяет, является ли заданное событие «срочным».

Канал события

tresult PLUGIN_API setChannel (IMEObjectID event, int channel)
int PLUGIN_API getChannel (IMEObjectID event)

Данные методы используются для определения канала заданного МИДИ события.

IMEAccessor::setChannel Задаёт канал событию
IMEAccessor::getChannel Определяет/возвращает канал заданного события

Состояние и данные события

MidiStatus PLUGIN_API getStatus (IMEObjectID event)
tresult PLUGIN_API setStatus (IMEObjectID event, MidiStatus status)
int PLUGIN_API getData1 (IMEObjectID event)
tresult PLUGIN_API setData1 (IMEObjectID event, int data)
int PLUGIN_API getData2 (IMEObjectID event)
tresult PLUGIN_API setData2 (IMEObjectID event, int data)
int PLUGIN_API getData3 (IMEObjectID event)
tresult PLUGIN_API setData3 (IMEObjectID event, int data)

Данные методы используются для определения статуса и данных (байтов) МИДИ сообщения.

IMEAccessor::getStatus Возвращает статус байта
IMEAccessor::setStatus Задаёт статус байта
IMEAccessor::getData1 Возвращает первый байт
IMEAccessor::setData1 Задаёт значение первого байта
IMEAccessor::getData2 Возвращает значение второго байта
IMEAccessor::setData2 Задаёт значение второго байта
IMEAccessor::getData3 Возвращает значение 3-го байта
IMEAccessor::setData3 Задаёт значение 3-го байта

Заметка: третий байт в миди сообщении используется для задания «уровня NoteOff  » (отпуская клавиши) миди события (ноты), обычно игнорируется

Точная настройка события

int PLUGIN_API getMicroTuning (IMEObjectID event)
tresult PLUGIN_API setMicroTuning (IMEObjectID event, int cent)

Данные методы используются для определения точной настройки МИДИ события. Максимальный диапазон = +/- 100 центов.

IMEAccessor::getMicroTuning Возвращает значение точной настройки (в центах)
IMEAccessor::setMicroTuning Задаёт значение точной настройки (в центах)

Точная настройка поддерживается не всеми ВСТ инструментами, пример поддерживающего - HALion.

IMEAccessor::getMidiChannel

long PLUGIN_API getMidiChannel ()

Возвращает заданный МИДИ канал дорожки. Возвращаемое значение может быть и «-1″ — если задан «любой» МИДИ канал.

Обработка интервала

long PLUGIN_API getInterval ()

tresult PLUGIN_API setInterval (long interval)

Данные методы используются для задания или расчёта интервала, в котором МИДИ сообщения обрабатываются (смотри IMidiEffect::playAction).

IMEAccessor::getInterval Возвращает размер интервала в миллисекундах
IMEAccessor::setInterval Задаёт размер интервала в миллисекундах

Заметка: метод IMEAccessor::setInterval работает вместе с методом IMEAccessor::setCanPlayInStop, обрабатываемый интервал может быть задан и в нём. Если второй метод  не имеет значения «ИСТИНА», то запрос первого метода ничего не даст.

IMEAccessor::getContextInfo

tresult PLUGIN_API getContextInfo (long ppq, MidiContextInfo* info)

Заполняет интерфейс MidiContextInfo метрическими данными заданной PPQ позиции, детальное описание дано в интерфейсе MidiContextInfo.

IMEAccessor::setCanPlayInStop

tresult PLUGIN_API setCanPlayInStop (int state)

Информирует хост-программу что плагин может играть в «режиме остановки». Данный запрос должен быть сделан во время инициализации процессов плагина. Видимо имеется ввиду то что плагин продолжит обрабатывать данные после того как пользователь остановит каретку в секвенсоре хост-программы.

IMEAccessor2

Наследственность: FUnknown <- IMEAccessor <- IMEAccessor2

Подключаемый файл: imidieffect.h

Вы можете запрашивать переменные, обрабатываемые в методе  IPluginBase::initialize, для отклика к этому интерфейсу.

Методы:

  • IMEAccessor2::receiveMidiEvent
  • IMEAccessor2::getSysexSize
  • IMEAccessor2::getSysexMessage

IMEAccessor2::receiveMidiEvent

tresult PLUGIN_API receiveMidiEvent (IMEObjectID event)

Посылает МИДИ событие к хост-программе.

Заметка: в кубейсе миди события могут быть записаны если выбрать порт  «MIDI Plug-In» в качестве входа МИДИ канала.

IMEAccessor2::getSysexSize

int PLUGIN_API getSysexSize (IMEObjectID event)

Возвращает размер данных SysE события (или ноль).

IMEAccessor2::getSysexMessage

void* PLUGIN_API getSysexMessage (IMEObjectID event)

Получает указатель к данным SysEx сообщения. Заметка: Если Вы хотите копировать SysEx данные, то используйте метод IMEAccessor2::getSysexSize.

Хост-интерфейсы МИДИ эффекта

IMasterTrackInfo

Наследственность: FUnknown <- IMasterTrackInfo

Подключаемый файл: imidieffect.h

Вы можете запрашивать обрабаатываемую в методе IPluginBase::initialize переменную для указания на данный интерфейс.

PPQ преобразования:

  • IMasterTrackInfo::getPpqBase
  • IMasterTrackInfo::seconds2ppq
  • IMasterTrackInfo::ppq2seconds

События темпа:

  • IMasterTrackInfo::countTempoEvents
  • IMasterTrackInfo::getTempo
  • IMasterTrackInfo::getTempoIndex

Информация о тактах:

  • IMasterTrackInfo::getBar
  • IMasterTrackInfo::getBarPosition

События подписи:

  • IMasterTrackInfo::countSignatureEvents
  • IMasterTrackInfo::getSignature
  • IMasterTrackInfo::getSignatureIndex

IMasterTrackInfo::getPpqBase

long PLUGIN_API getPpqBase ()

Возвращает текущее значение PPQ из хост-программы.

IMasterTrackInfo::seconds2ppq

long PLUGIN_API seconds2ppq (long seconds)

Конвертирует из миллисекунд в PPQ.

IMasterTrackInfo::ppq2seconds

long PLUGIN_API ppq2seconds (long ppq)

Конвертирует из PPQ в миллисекунды.

IMasterTrackInfo::countTempoEvents

long PLUGIN_API countTempoEvents ()

Подсчитывает колличество изменений темпа в текущей секвенции хост-программы.

IMasterTrackInfo::getTempo

tresult PLUGIN_API getTempo (long index, long* ppq, long* bpm)

Определяет темп, его изменение. Точка изменения темпа передаётся с помощью PPQ.

IMasterTrackInfo::getTempoIndex

long PLUGIN_API getTempoIndex (long ppq)

Получает загаловок таблицы изменений темпа в PPQ (что за таблицы не уточняется).

IMasterTrackInfo::getBar

tresult PLUGIN_API getBar ( long ppq, long* barCount, long* beatCount, long* remainder)

Получает информацию о такте в заданной позиции PPQ.

IMasterTrackInfo::getBarPosition

long PLUGIN_API getBarPosition (long barCount, long beatCount, long remainder)

Получает информацию о PPQ позиции заданного такта.

IMasterTrackInfo::countSignatureEvents

long PLUGIN_API countSignatureEvents ()

Возвращает колличество событий подписи в текущей секвенции хост-программы.

IMasterTrackInfo::getSignature

tresult PLUGIN_API getSignature ( long index, long* numerator, long* denominator)

Получает информацию о подписи из заданного загаловка таблицы подписей.

IMasterTrackInfo::getSignatureIndex

long PLUGIN_API getSignatureIndex (long ppq)

Получает загаловок таблицы подписей заданной PPQ позиции.

ITransportInfo

Наследственность: FUnknown <- ITransportInfo

Подключаемый файл: imidieffect.h

Вы можете запрашивать обрабатываемую в методе IPluginBase::initialize переменную для ссылки/рефера на этот интерфейс.

Методы:

  • ITransportInfo::getSystemTimePpq

ITransportInfo::getSystemTimePpq

long PLUGIN_API getSystemTimePpq ()

Возвращает текущее системное время секвенсора хост-программы в значениях PPQ.

МИДИ классы языка С++

CMidiEffect

Наследственность: IMidiEffect, IGenericPlugController, IPersistentChunk, IUIFactory2, IViewFactory

Подключаемый файл: midieffect.h

Данный интерфейс является классом языка С++, который дополняет необходимые для реализации МИДИ эффектов интерфейсы. Вы можете описать Ваш собственный класс в нём.

Участники: CPlugParams — данный участник хранит параметры объектов МИДИ эффекта.

Дополнительные методы:  CMidiEffect::presetsAreChunks

Конструктор

CMidiEffect::CMidiEffect (const char* storageUID = 0)

При использовании chunk памяти (о ней писалось в первой части руководства), идентификатор класса нуждается в ассоциации данных памяти с МИДИ эффектом (смотри IPersistentChunk).

Конфигурация

presetsAreChunks (bool) Консультации IPersistentChunk у хост-программы. Запрашивается  в  Ctor описанного класса (смотри также первую часть руководства — «хранение плагинов»).
setResourceID (const CResourceID&) Задаёт идентификатор ресурсов редактора (под редактором понимается внешний вид плагина) XML скрипт. Запрашивается  в  Ctor описанного класса (смотри также первую часть руководства — «написание GUI используя XML»).

CMidiEventQueue

Наследственность: CLinkedList <- CMidiEventQueue

Подключаемый файл: eventqueue.h

Интерфейс CMidiEventQueue является отсортированой по времени очередью МИДИ событий,  в которой хранятся события типа IMEObjectID. Для корректной работы требуется интерфейсIMEAccessor .

Методы:

  • CMidiEventQueue::setAccessor
  • CMidiEventQueue::addEvent
  • CMidiEventQueue::addEventCopy
  • CMidiEventQueue::retrieveEvent
  • CMidiEventQueue::empty

CMidiEventQueue::setAccessor

void setAccessor (IMEAccessor* a)

Задаёт новый доступ к очереди.

CMidiEventQueue::addEvent

bool addEvent (IMEObjectID event)

Добавляет одно событие в упорядоченную по времени очередь.

CMidiEventQueue::addEventCopy

bool addEventCopy (IMEObjectID event)

Добавляет копию события в упорядоченную по времени очередь.

CMidiEventQueue::retrieveEvent

IMEObjectID retrieveEvent (long toPPQ)

Возвращает (и вырезает) первое событие с меткой меньшей чем toPPQ. Заметка: все события, созданные внутри плагина, должны быть уничтожены/удалены с помощью метода IMEAccessor::destroyEvent.

_CMidiEventQueue::empty

void empty ()

Очищает очередь. Заметка: все события, созданные внутри плагина, должны быть уничтожены/удалены с помощью метода IMEAccessor::destroyEvent.

Вы должны быть залогинены для комментирования.