Está en la página 1de 27

CAPTULO 6 - DSP Y PLUGINS VST

6.0 Introduccin
La interfaz Tecnologa de Estudio Virtual o VST (del ingls Virtual Studio Technology)
fue desarrollada por la compaa Steinberg Media GmbH y lanzada al pblico en 1996
como parte de la aplicacin Cubase 3.0. El VST es una interfaz estndar de efectos de
audio que le permite a los programadores crear un estudio virtual en su computadora a
travs del uso de cualquier Secuenciador o Ambiente de Audio Digital que trabaje con
VSTs. Existen muchos secuenciadores en el mercado que soportan la integracin VST,
como son: Cubase, Nuendo y Logic Audio [14, 15].

El estndar VST 1.0 permite extender la funcionalidad de una aplicacin de audio al
definir nuevas unidades de procesamiento de audio que agregan la funcionalidad de los
secuenciadores. Estos plugins pueden tener un nmero arbitrario de entradas y salidas as
como procesar los datos de audio en casi cualquier manera deseada.

Probablemente la adicin ms importante en el VST 2 ha sido el soporte de la produccin
de eventos MIDI. La razn de esto es que los sintetizadores de msica son esencialmente
mdulos controlados por MIDI que producen datos de audio, y como el programador es
libre de hacer casi cualquier cosa con el VST, es posible la produccin de tonos
musicales [16].

Los VSTs se presentan en forma de procesadores de efectos, mquinas de ritmos,
sintetizadores, y otras herramientas de produccin de audio existentes. Muchos de estos
VSTs emulan herramientas reales ya existentes o implementan innovaciones que el
programador desea. Los efectos y sintetizadores VST son plugins que uno puede cargar
en muchos programas y secuenciadores de edicin de audio, actualmente son utilizados
en plataformas Windows y Apple Macintosh.

Los VSTs no requieren de ningn dispositivo externo para funcionar, por lo mismo no se
tiene que utilizar espacio extra o cables para trabajar con ellos. Considerando que el VST
est ruteado al secuenciador a travs de conexiones virtuales, no existe ninguna prdida
de sonido cuando se realizan las mezclas. Adems los VSTs son completamente
automatizables, lo que significa que los controles pueden ser modificados en tiempo real,
o pueden rutearse y ser controlados por controladores externos.

Otro aspecto importante de los VSTs es que se trata de un estndar libre, lo que permite
que todos los programadores y compaas desarrollen nuevos VSTs de manera gratuita y
libre, lo que conlleva un desarrollo muy gil de esta tecnologa [14, 15].


6.1 Funcionamiento del Estndar VST
En esta parte se presenta una investigacin a profundidad del desarrollo de una serie de
aplicaciones de audio basadas en el SDK de Steinberg (Kit de Desarrollo de Software o
Software Development Kit en ingls, que es un paquete distribuido para permitir crear
software terceros, conocidos como 3rd party, para una plataforma particular) que operan
64
bajo el sistema basado en host VST de Steinberg, detallando los mtodos utilizados y la
efectividad de los resultados alcanzados.

El host VST es una tecnologa muy nueva, y as como cualquier nueva tecnologa en este
campo que es lanzada, su desarrollo est a menudo ya en marcha para sistemas mejorados
con la cual sustituirla. De esta manera, se tiene poco tiempo para mirar atrs a los
orgenes de los sistemas contemporneos, donde una retrospectiva puede ayudar para una
comprensin ms profunda de los mtodos actuales.

Los VSTs pueden ser considerados plugins debido a su modo de funcionamiento, pues un
plugin es definido como un archivo que tiene datos utilizados para alterar, mejorar o
extender la operacin de un parmetro de un programa de aplicacin. Los plugins
usualmente se basan en dos conceptos clave: funciones recursivas y polimorfismos.

Las funciones recursivas son funciones que define un programador pero que son llamadas
desde afuera del cdigo del programa. En lenguaje C son implementadas con la ayuda de
apuntadores a funciones. El polimorfismo es parecido a una funcin recursiva pero
orientada a objetos, en la cual en vez de definir un apuntador a una funcin con un
prototipo especfico, se sobreponen un grupo de funciones virtuales desde una clase base.

La implementacin concreta en la mayora de las plataformas que soportan el manejo de
VSTs es un tipo de libreras de enlace dinmico o DLL (del ingls Dynamic Link
Library) cuyos contenidos son cargados dinmicamente durante la ejecucin, lo cual es
equivalente funcionalmente al polimorfismo.

Adems del polimorfismo, el enlace dinmico tiene otras ventajas. Como los plugins
pueden agregados y retirados segn se dese, la actualizacin del cdigo del plugin es
posible sin tener que tocar el cdigo de host. Cada instancia de la librera es mapeada al
espacio de direccin del proceso del cliente, y todos los recursos localizados por la
librera son tomados por el cliente.

Para el host, los plugins son simples cajas negras con un nmero arbitrario de entradas y
salidas de audio o MIDI. Debido a que los plugins no estn consientes de su contexto, es
posible y muy bien soportado el encadenar varios plugins o rutear muchas fuentes
diferentes de audio hacia un solo plugin [16].

6.2 Consideraciones Especiales con VSTs
Antes de empezar la programacin de VSTs se deben tomar en cuenta diversos factores
que interfieren ligeramente con el funcionamiento ideal de un VST, debido a que podra
llegarse a pensar que existe un problema de programacin; sin embargo, es un
comportamiento normal. A continuacin se explican los diversos factores que se deben
considerar.

6.2.1 Latencia de Audio
Idealmente, cuando se toca un instrumento virtual, no debera responder de manera
diferente a un sintetizador de hardware dedicado. El tiempo entre la entrada de la nota
65
MIDI y el momento en que el sintetizador responde es conocido como latencia y es
importante mantenerla en un nivel mnimo. Usualmente, los sintetizadores de hardware
responden en un tiempo cercano a 5ms, las latencias de 20ms o ms pueden ser
percibidas como un claro retraso. La latencia depende de los estndares del driver de
audio soportados por el host y el timing MIDI en el sistema operativo.

En este proyecto se decidi trabajar con controladores ASIO con los cuales se logra tener
la adquisicin de datos en tiempo real, al bajar la latencia y configurar ms parmetros de
la tarjeta de sonido de la computadora [17].

En el men de configuracin se pueden definir parmetros como el tamao del buffer
ASIO, nmero de muestras tomadas en la adquisicin de datos, frecuencias de
preferencia, etc. Con todo esto se logra tener un gran control sobre las caractersticas del
sonido de entrada a la computadora con lo que se espera tener una respuesta en tiempo
real (baja latencia, pero con buena calidad).


Fig. 6.1 Men de Configuracin de Controladores ASIO [17].

No todos los efectos pueden correr en tiempo real, an cuando los hosts VST ofrecen la
opcin de definir un retraso de procesamiento inicial. El afectar el tiempo de entrada de la
seal o acelerarla sin cambiar su pitch (como efecto de la aceleracin) requerira de una
gran cantidad de muestras guardadas en un buffer comparado con el procesamiento
convencional de audio. Es por esto que el estndar VST 2 agrega la opcin de
procesamiento off-line (como el que facilitan los controladores ASIO), el cual permite
que el plugin lea el archivo de audio que el host ha abierto sin detenerse por otras
aplicaciones que estn en ejecucin.

6.2.2 Estabilidad
Este es un requerimiento esencial en la produccin, ejecucin y grabacin de audio en un
estudio. Los hosts VST deben trabajar casi en tiempo real porque el no ser capaz de
realizar un proceso en tiempo puede resultar en una prdida de audio lo que detendra por
completo al host de audio o dems artefactos de produccin. Afortunadamente, la
programacin de VSTs se enfoca al procesamiento de audio, de forma que la complejidad
66
es reducida radicalmente comparada con el programa de un instrumento independiente
con su propia implementacin de audio y MIDI.

6.2.3 Portabilidad e Interfaz de Usuario
El SDK de los VSTs ha sido creado con la opcin de funcionar en varias plataformas
como Windows, SGU y BeOS, entre otros. De todas maneras, segn la documentacin
VST, el cdigo de la Interfaz Grfica de Usuario (GUI, Grafic User Interface) es
adaptable rpidamente para cumplir las especificaciones de las plataformas.

6.2.4 Automatizacin y Manejo Preestablecido
El SDK de los VSTs permite al desarrollador crear soporte preestablecido en el plugin, lo
que significa que el host puede salvar o cargar configuraciones del instrumento. Tambin
se soporta la automatizacin, la cual es capaz de grabar cambios en los parmetros
relacionados con el plugin directamente en el secuenciador.

6.2.5 Localizacin
La localizacin es muy rara en caso de los instrumentos VST (VSTi), an en los VSTs
comerciales. Algunos factores que contribuyen con la rareza de la localizacin que es el
hecho de que los instrumentos de hardware son virtualmente ilocalizables y la mayora
de los usuarios de plugins deben saber ingls a cierto nivel. La falta de localizacin
significa que la mayora de los nombres de los parmetros y otras instrucciones han sido
codificadas, al menos en los ejemplos del SDK de los VSTs y todos los instrumentos
gratuitos.

6.2.6 Lneas de Componentes y Productos
Tal como en otras marcas o compaas de produccin de software, las firmas grandes en
el mercado parecen contar con componentes reutilizables. Esto tiende a ser una solucin
comn para el procesamiento de audio o manejo MIDI, pero tambin es comn el querer
tener o desarrollar una Interfaz de Usuario (UI, User Interface) completamente porttil,
configurable y que funcione en diferentes plataformas como Windows y Mac [16].


6.3 Programacin de un Plugin VST
Para ser capaz de desarrollar un plugin VST se tiene que crear un archivo DLL, el cual
puede ser colocado en la carpeta de VSTs a la cual recurre nuestra aplicacin host. Para
poder proceder a la programacin de un VST se debe contar con un compilador como
Microsoft Visual C++6.0, en el cual se agregan los archivos bsicos de un programa en
C, con las extensiones *.cpp, *.c, *.hpp o *.h.

Adems, es preferible contar con una aplicacin que nos permita evaluar el
funcionamiento de nuestros plugins, como puede ser Audacity, Cubase, WaveLab, Fruity
Loops, etc. con esto se puede comprobar que estn trabajando como fueron concebidos,
de lo contrario, se debe regresar a la etapa de diseo y modificar el programa hasta
obtener los resultados esperados.


67
6.3.1 Preparacin del Ambiente SDK
Como primer paso, se debe bajar el SDK de los VST, disponible en la pgina de
Steinberg (se incluyen los SDK versin 2.3 y 2.4 en el Apndice C de este documento),
actualmente se ha desarrollado la versin 2.4 del SDK; sin embargo, el desarrollo de los
efectos que realic se bas en la versin 2.3 del mismo. Posteriormente, se debe indicar la
ubicacin de dicho flder a Microsoft Visual C++6.0 cuando se inicia un nuevo proyecto
VST.

Ahora, se le debe indicar a Visual Studio la nueva ruta a incluir para los archivos de
encabezado y cpp de los VSTs, siguiendo los siguientes pasos:
1. Abrir Visual C++y dar clic en el men de Tools
2. Seleccionar la carpeta de Projects, y luego VC++ Directories
3. Seleccionar Include Files en el men de Show Directories For
4. Ingresar la rita de la localizacin de los archivos de encabezado. En caso de que
se tenga la configuracin predefinida, la ruta debe ser algo como: C:\Program
Files\Microsoft Visual Studio\VST_SDK2.3\vstsdk2.3\source\common , aunque no
es necesario que se tenga la misma ruta, puede ser cualquiera.
5. Se debe mover esta ruta hasta el fondo de la lista, ya que VC++busca por los
archivos incluidos en el orden listado.

6.3.2 Procesamiento de Audio Bsico
Para empezar, se debe tener claro lo que se va a hacer basado en el funcionamiento de un
VST, sabiendo lo que es, cmo trabaja y lo que uno como programador debe realizar. En
Windows, un VST es un archivo DLL (Dynamic Link Library). En Mac, un VST es un
Bundle o un Code Resource. En BeOS, es una carpeta compartida. En este caso, se
trabaja en el desarrollo de un DLL para Windows.

Un DLL es una coleccin de funciones o datos que pueden ser utilizados por otro
programa de mayores dimensiones. La parte dinmica significa que est disponible para
ser utilizado segn sea requerido. La aplicacin o host VST crea un enlace al DLL y
entonces puede acceder a estas funciones.

La creacin del DLL es fcil, debido a que un VST requiere dos funciones bsicas,
llamadas process() y processReplacing(). La funcin process() procesa datos de audio y
entonces los agrega al buffer de salida, a este proceso se le llama Acumular. La
funcin processReplacing() procesa datos de audio y entonces reemplaza al buffer de
salida con los datos procesados, a este proceso se le llama Reemplazar. La diferencia
es que se utiliza el Acumulado para agregar informacin, y el Reemplazado para
reemplazar la informacin [18].

Las aplicaciones de host que reproducen archivos de audio son a menudo modeladas
segn el diseo clsico. El audio es enviado desde el disco duro a travs de un canal
mezclador, y ese canal incluye ajustes asociados de "Enviar" e "Insertar" de manera
similar a los mezcladores de hardware. Es en estos dos puntos que el efecto del plugin
puede ser agregado.

68
El efecto "Enviar" permite a una porcin del sonido entrante ser comunicado al plugin
mientras el audio no afectado contina fluyendo directo a la salida maestra. Esto permite
al sonido utilizado ser controlado por el control enviar. En contraste, el efecto "Insertar"
manda la seal completa de audio a travs del plugin, quitando cualquier control de
balance.


Fig. 6.2 Operaciones Enviar e Insertar en host Cubase SX3.

La eleccin de la operacin Enviar o Insertar en el cdigo del plugin se logra utilizando
las funciones process() y processReplacing(), ya mencionadas, mantenidas en los
archivos de encabezado de audioeffect y audioeffectx del SDK. Estas funciones actan
como intermediarias entre el plugin y el host, y realmente representan a los efectos
Enviar e Insertar, respectivamente [19].


Fig. 6.3 Diagrama a Bloques de la Ruta en el Mezclador Virtual [19].

Con esto, lo que se hace es enviar una parte de la salida al efecto, y re-mezclarlo despus
con la parte no procesada. Naturalmente, no se quiere reemplazar todo el buffer de salida
en este caso, slo se suman los datos procesados con los datos en el buffer de salida.
Realmente, slo se debe de incluir la funcin process(), pero es adecuado el incluir ambas
funciones.

69
El VST trabaja de manera muy simple: una de las dos funciones es llamada, y dados dos
buffers de entrada (estreo) de datos de audio, la funcin procesa esos datos y escribe el
resultado en los dos buffers de salida (estreo). Los datos de audio estn dados como un
arreglo de nmeros de punto flotante, con magnitudes que van de -1.0 a +1.0 inclusivo.
Cada nmero representa la amplitud de una muestra de seal de audio.


Fig. 6.4 Seal de Audio a ser Muestreada [18].

En la Figura 6.4 se muestra una seal senoidal simple, la cual completa un ciclo despus
de 1 segundo. Se establece que la tasa de muestreo de audio es de 44,100kHz (lo que
indica que la seal es muestreada 44,100 veces cada segundo); entonces, en la primer
muestra el valor muestreado debe ser cercano a 0.0; la muestra en 0.25 segundos debe ser
cercana a 1.0; la muestra en 0.5 segundos debe ser casi de 0.0, y as sucesivamente.

Si se hace esto 44,100 veces por segundo, se tendr una lista de 44,100 nmeros de punto
flotante con valores dentro del rango de -1.0 a +1.0. Y esto es precisamente lo que el host
VST estar realizando, aunque no se puede asumir que se tendr un nmero de muestras
en particular. Lo que se hace con estos nmeros es encaminarlos a un algoritmo y
entregar el resultado de regreso al host, el cual realiza un poco ms de procesamiento a
travs de la cadena de VSTs y otros efectos y mezclas, enviando finalmente una cadena
de nmeros hacia la tarjeta de sonido de la computadora, o escribindolas en el disco.

Para comprender esto, se puede tomar como ejemplo un VST sencillo que silencia el
sonido (un efecto mute, tomado del Tutorial de plugins VST de Rick Strom [18]), donde
simplemente se toma lo que est en el buffer de entrada y se escribe una cantidad igual de
ceros en el buffer de salida.

whi l e ( l ef t _i nput _buf f er ++ ! = NULL) {
l ef t _out put _buf f er ++ = 0. 0;
r i ght _out put _buf f er ++ = 0. 0;
}

Tambin, sencillamente, se pueden invertir los canales estreo, al escribir lo que est en
el buffer de entrada izquierdo en el buffer de salida derecho, y viceversa:

whi l e ( l ef t _i nput _buf f er ! = NULL) {
l ef t _out put _buf f er ++ = r i ght _i nput _buf f er ++;
70
r i ght _out put _buf f er ++ = l ef t _i nput _buf f er ++;
}

6.3.3 Estructura de Clases VST
Debido a que se est utilizando C++ para desarrollar los plugins, stos deben ser
implementados como una clase. Afortunadamente, no se necesita construir toda la clase
desde el inicio, ya que el SDK provee de una clase bsica a completar: AudioEffectX
[18].

Visto desde un punto de programacin orientada a objetos, la clase audioeffectx tiene el
problema de que las funciones de call-back que son llamadas por el host y la que utiliza
el plugin para marcar las propiedades de (o la accin de peticin desde) el host estn en la
misma clase. Como una consecuencia directa, uno puede accidentalmente tomar control
sobre los mtodos host-to-plug que no son del todo necesarios. Y debido a que no se tiene
una convencin universal de nombres para tener nombres bien establecidos, el
programador constantemente debe recurrir al SDK para ver el tipo de mtodo en cuestin.
Finalmente, mezclar mtodos virtuales y mtodos prcticamente no-virtuales en la misma
clase hace que la estructura sea ms compleja y difcil de entender [16].

6.3.4 Desarrollo de un Plugin sin Efecto
Ahora, para ser capaz de desarrollar un plugin es necesario conocer los tres archivos
bsicos que lo componen, como son: AMute.cpp, AMuteMain.cpp y AMute.hpp. Con
estos archivos se crear un plugin que servir de base para comprender la programacin,
pues es til tener archivos base, sobre los cuales escribir y ahorrar tiempo.

Este plugin simplemente toma un buffer de audio y le coloca ceros, silenciando cualquier
tipo de datos que reciba. A continuacin se muestra el archivo de clase AMute
(AMute.hpp):

cl ass AMut e : publ i c Audi oEf f ect X
{
publ i c:
AMut e ( audi oMast er Cal l back audi oMast er ) ;
~AMut e ( ) ;

/ / Pr ocesses
vi r t ual voi d pr ocess ( f l oat **i nput s, f l oat **out put s, l ong
sampl eFr ames) ;
vi r t ual voi d pr ocessRepl aci ng ( f l oat **i nput s, f l oat
**out put s, l ong sampl eFr ames) ;
/ / Pr ogr am

vi r t ual voi d set Pr ogr amName ( char *name) ;
vi r t ual voi d get Pr ogr amName ( char *name) ;
vi r t ual bool get Ef f ect Name ( char * name) ;
vi r t ual bool get Vendor St r i ng ( char * t ext ) ;
vi r t ual bool get Pr oduct St r i ng ( char * t ext ) ;
vi r t ual l ong get Vendor Ver si on ( ) { r et ur n 1000; }
vi r t ual Vst Pl ugCat egor y get Pl ugCat egor y ( ) { r et ur n
kPl ugCat egEf f ect ; }

71
pr ot ect ed:
char pr ogr amName[ 32] ;
};

Lo que hace esta parte del cdigo es decirle al compilador que se extender la clase
existente AudioEffectX (la clase base) para tener una clase ms pequea llamada AMute.
Entonces se declaran las funciones y variables necesarias para su funcionamiento:
process y processReplacing realizarn el procesamiento real en el buffer de audio
setProgramName y getProgramName colocarn y obtendrn el nombre del
programa. En un VST, el programador es capaz de especificar un nmero de
parmetros, usualmente representados en el host como una perilla o un slider, que
puede ser utilizado para enviar datos al plugin para controlar la afectacin del
audio. Un programa es simplemente un conjunto de valores para estos parmetros
getEffectName, getVendorString, getProductString y getVendorVersion
simplemente permiten al host pedir cierta informacin sobre el plugin
getPlugCategory regresa el tipo de plugin que se est escribiendo. En este caso, se
est escribiendo un efecto, entonces se regresa kPlugCategEffect. Esto es
necesario para que el host sepa que no se est escribiendo un VSTi (instrumento
VST)

Ahora, se debe definir el plugin definiendo sus funciones, para esto es el archivo
AMute.cpp:

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AMut e: : AMut e ( audi oMast er Cal l back audi oMast er )
: Audi oEf f ect X ( audi oMast er , 1, 0) / / 1 pr ogr am, 0
par amet er s
{
set NumI nput s ( 2) ; / / st er eo i n
set NumOut put s ( 2) ; / / st er eo out
set Uni queI D ( GDMt ) ; / / i dent i f y
canMono ( ) ; / / makes sense t o f eed bot h
i nput s wi t h t he same si gnal
canPr ocessRepl aci ng ( ) ; / / suppor t s bot h
accumul at i ng and r epl aci ng out put
st r cpy ( pr ogr amName, " Def aul t " ) ; / / def aul t pr ogr amname
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AMut e: : ~AMut e ( )
{
/ / not hi ng t o do her e
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AMut e: : set Pr ogr amName ( char *name)
{
st r cpy ( pr ogr amName, name) ;
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AMut e: : get Pr ogr amName ( char *name)
72
{
st r cpy ( name, pr ogr amName) ;
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AMut e: : get Ef f ect Name ( char * name)
{
st r cpy ( name, " Gl owdot Mut e" ) ;
r et ur n t r ue;
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AMut e: : get Pr oduct St r i ng ( char * t ext )
{
st r cpy ( t ext , " Gl owdot Mut e" ) ;
r et ur n t r ue;
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AMut e: : get Vendor St r i ng ( char * t ext )
{
st r cpy ( t ext , " Gl owdot Pr oduct i ons" ) ;
r et ur n t r ue;
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AMut e: : pr ocess ( f l oat **i nput s, f l oat **out put s, l ong
sampl eFr ames)
{
f l oat *i n1 = i nput s[ 0] ;
f l oat *i n2 = i nput s[ 1] ;
f l oat *out 1 = out put s[ 0] ;
f l oat *out 2 = out put s[ 1] ;

whi l e ( - - sampl eFr ames >= 0)
{
( *out 1++) += 0; / / accumul at i ng
( *out 2++) += 0;
}
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AMut e: : pr ocessRepl aci ng ( f l oat **i nput s, f l oat **out put s,
l ong sampl eFr ames)
{
f l oat *i n1 = i nput s[ 0] ;
f l oat *i n2 = i nput s[ 1] ;
f l oat *out 1 = out put s[ 0] ;
f l oat *out 2 = out put s[ 1] ;

whi l e ( - - sampl eFr ames >= 0)
{
( *out 1++) = 0; / / r epl aci ng
( *out 2++) = 0;
}
}
73

Esta parte del cdigo es ms larga que la anterior, pero se puede notar que la mayora de
las funciones estn copiando una cadena de caracteres de un lugar a otro. Lo que se debe
explicar es el constructor y las funciones process() y processReplacing(). El constructor
respeta los pasos siguientes:
acepta un objeto audioMasterCallback que viene del host e inmediatamente lo
entrega a la clase base
declara un nmero de programas y parmetros necesarios. En este caso, un
programa, cero parmetros
declara el nmero de entradas y de salidas que se necesitan. Normalmente se
requieren dos entradas y una salida, o una entrada y dos salidas
establece un identificador nico de cuatro caracteres para el plugin, para que el
host sepa de l
llama a canMono() para que el host sepa que las seales mono-aurales estn
permitidas para este plugin
llama a canProcessReplacing para que el host sepa que este efecto puede
funcionar como un efecto de Envo (de las operaciones Enviar e Insertar
explicadas previamente)
establece el nombre del programa preestablecido

Ahora se deben revisar las funciones process() y processReplacing(). Ambas realizan la
misma tarea bsicamente, slo una de ellas establece la salida a que sea igual a los datos
procesados, y la otra agrega los datos procesados a la salida. Para comprender la
diferencia, se debe analizar el funcionamiento del plugin. Si el efecto va a ser como un
efecto de Envo, el host optar por seleccionar la funcin process(). En el caso de que el
efecto sea parte de una cadena de efectos, el host optar por la funcin
processReplacing().

Simplemente una reemplaza el buffer de salida con los datos procesados, y la otra lo
agrega (acumula). Entonces, para el efecto AMute, la funcin process() agrega un cero a
cada celda del buffer de salida, mientras que processReplacing() sobrescribe cada celda
con un cero.

Finalmente, se debe revisar la funcin principal (en AMuteMain.cpp):

AEf f ect *mai n ( audi oMast er Cal l back audi oMast er )
{
/ / Get VST Ver si on
i f ( ! audi oMast er ( 0, audi oMast er Ver si on, 0, 0, 0, 0) )
r et ur n 0; / / ol d ver si on
/ / Cr eat e t he Audi oEf f ect
AMut e* ef f ect = new AMut e ( audi oMast er ) ;
i f ( ! ef f ect )
r et ur n 0;
/ / Check i f no pr obl emi n const r uct or of AMut e
i f ( oome)
{
del et e ef f ect ;
r et ur n 0;
74
}
r et ur n ef f ect - >get Aef f ect ( ) ;
}

Lo que el cdigo realiza aqu es simplemente:
acepta el objeto audioMasterCallback que viene del host
revisa la versin del VST y se asegura de que sea compatible
crea un objeto para la clase AMute, pasndola al objeto audioMasterCallback
realiza un chequeo estndar de error

Este cdigo puede ser compilado, con lo que se genera el archivo DLL con el mismo
nombre de las funciones. Este archivo DLL debe ser movido a la carpeta de los VSTs
para ser reconocido por el host. Dentro del host se puede probar el plugin activndolo y
desactivndolo, con lo que se podr observar que se silencia el sonido al activarse.

6.3.5 Agregando Parmetros al Efecto
Los parmetros permiten al usuario tener control sobre la afectacin del sonido que ser
modificado por el plugin. No es necesario que el programador dibuje las perillas o
sliders, simplemente se deben declarar y definir, y el host se encarga de asignar dibujos
predeterminados. Lo que se necesita hacer es lo siguiente:
decirle al host cuntos parmetros se necesitan
proveer una funcin getParameterLabel para que el host sepa como etiquetar al
parmetro
proveer las funciones setParameter y getParameter para obtener y establecer el
valor del parmetro
proveer una funcin getParameterDisplay para desplegar el valor real del
parmetro, porque todos los parmetros son representados como valores flotantes
entre 0.0 y 1.0, inclusivo. Obviamente, no todo valor corresponder a esos
valores, por lo que deben traducirse a valores reales cuando el host despliega el
valor actual por debajo de la perilla o slider, y cuando se utiliza este valor en las
funciones process() y processReplacing()

Lo que se har es agregar dos parmetros al plugin y controlar algo con ellos,
convirtiendo el efecto AMute en AGapper (sugerido tambin en el Tutorial de Rick
Strom [18]), dejando que el usuario decida cuntas muestras sern escuchadas y cuntas
sern silenciadas en cada ciclo. Se agregar un parmetro llamado Pause Duration, el
cual ser el nmero de muestras que se escucharn en cada ciclo, y Mute Duration, el
cual ser el nmero de muestras que se silenciarn en cada ciclo. Tambin se deben
determinar los valores que tomarn los parmetros.

Se puede definir un rango de valores de 0 a 10,000 para cada parmetro, lo que permitir
un ciclo mximo de 20,000 muestras. Esto podra no ser un Gapper en el sentido usual,
porque un verdadero Gapper debera insertar silencio en el flujo del sonido, en vez de
sobrescribir partes con silencio en l. Se puede utilizar la clase AMute como base, pero
con nuevos nombres, como: AGapper.cpp, AGapperMain.cpp y AGapper.hpp. Se
analizar el archivo de encabezado AGapper.hpp:
75

cl ass AGapper : publ i c Audi oEf f ect X
{
publ i c:
AGapper ( audi oMast er Cal l back audi oMast er ) ;
~AGapper ( ) ;

/ / Pr ocesses
vi r t ual voi d pr ocess ( f l oat **i nput s, f l oat **out put s, l ong
sampl eFr ames) ;
vi r t ual voi d pr ocessRepl aci ng ( f l oat **i nput s, f l oat
**out put s, l ong sampl eFr ames) ;

/ / Pr ogr am
vi r t ual voi d set Pr ogr amName ( char *name) ;
vi r t ual voi d get Pr ogr amName ( char *name) ;

/ / Par amet er
vi r t ual voi d set Par amet er ( l ong i ndex, f l oat val ue) ;
vi r t ual f l oat get Par amet er ( l ong i ndex) ;
vi r t ual voi d get Par amet er Label ( l ong i ndex, char *l abel ) ;
vi r t ual voi d get Par amet er Di spl ay ( l ong i ndex, char *t ext ) ;
vi r t ual voi d get Par amet er Name ( l ong i ndex, char *t ext ) ;
vi r t ual bool get Ef f ect Name ( char * name) ;
vi r t ual bool get Vendor St r i ng ( char * t ext ) ;
vi r t ual bool get Pr oduct St r i ng ( char * t ext ) ;
vi r t ual l ong get Vendor Ver si on ( ) { r et ur n 1000; }
vi r t ual Vst Pl ugCat egor y get Pl ugCat egor y ( ) { r et ur n
kPl ugCat egEf f ect ; }

pr ot ect ed:
f l oat f Pause, f Mut e;
bool bMut i ng; / / t r ue i f we ar e i n a mut i ng cycl e
i nt sampl eCount ;
char pr ogr amName[ 32] ;
};

Se han agregado las declaraciones par alas cinco funciones nuevas relacionadas con los
parmetros, adems de unas pocas variables nuevas.
fPause y fMute mantendrn los valores crudos de los parmetros
bMuting mantendr el estado en el que se est: cierto si se est silenciando,
falso en el otro caso
sampleCount toma en cuenta el nmero de muestras que se han procesado en cada
ciclo

Ahora se revisarn las definiciones, en AGapper.cpp, donde se analizarn las nuevas
funciones y las pequeas modificaciones al constructor. Primero, el constructor:

AGapper : : AGapper ( audi oMast er Cal l back audi oMast er )
: Audi oEf f ect X ( audi oMast er , 1, 2) / / 1 pr ogr am, 2
par amet er s
{
set NumI nput s ( 2) ; / / st er eo i n
set NumOut put s ( 2) ; / / st er eo out
76
set Uni queI D ( GDGp) ; / / i dent i f y
canMono ( ) ; / / makes sense t o f eed bot h
i nput s wi t h t he same si gnal
canPr ocessRepl aci ng ( ) ; / / suppor t s bot h
accumul at i ng and r epl aci ng out put
st r cpy ( pr ogr amName, " Def aul t " ) ; / / def aul t pr ogr amname
f Mut e = 0. 1f ; / / def aul t mut e per i od
t o 1000 sampl es ( . 1 * 10, 000)
f Pause = 0. 1f ; / / def aul t pause
per i od t o 1000 sampl es ( . 1 * 10, 000)
bMut i ng = f al se; / / st ar t i n non mut i ng cycl e
sampl eCount = 0; / / r eset sampl e count er
}

Aqu slo se ha cambiado el llamado al constructor de la clase base (AudioEffectX) para
especificar que slo se quieren dos parmetros. Tambin se inicializaron las nuevas
variables. Ahora, las definiciones de las nuevas funciones:

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AGapper : : set Par amet er ( l ong i ndex, f l oat val ue)
{
i f ( i ndex == 0)
f Mut e = val ue;
el se i f ( i ndex == 1)
f Pause = val ue;
}
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
f l oat AGapper : : get Par amet er ( l ong i ndex)
{
i f ( i ndex == 0)
r et ur n f Mut e;
el se i f ( i ndex == 1)
r et ur n f Pause;
el se r et ur n - 1. 0f ;
}
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AGapper : : get Par amet er Name ( l ong i ndex, char *l abel )
{
i f ( i ndex == 0)
st r cpy ( l abel , " Mut e Dur at i on" ) ;
el se i f ( i ndex == 1)
st r cpy ( l abel , " Pause Dur at i on" ) ;
}
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AGapper : : get Par amet er Di spl ay ( l ong i ndex, char *t ext )
{
i f ( i ndex == 0)
spr i nt f ( t ext , " %. 0f " , 10000 * f Mut e) ;
el se i f ( i ndex == 1)
spr i nt f ( t ext , " %. 0f " , 10000 * f Pause) ;
}
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AGapper : : get Par amet er Label ( l ong i ndex, char *l abel )
{
st r cpy ( l abel , " sampl es" ) ;
}
77

Lo ms importante a destacar es que los parmetros son definidos por un valor ndice, un
entero entre 0 y el nmero de parmetros menos 1. Se necesita tener cuidado con cul
parmetros es cul. El host dispondr de un nmero para indicar cada funcin que est
relacionada con un parmetro para identificarla.

Se debe notar que getParameter regresa el valor crudo y sin modificar del parmetro (el
valor entre 0.0 y 1.0), mientras que getParameterDisplay regresa el valor traducido al
nmero real, que est entre 0 y 10,000. Finalmente, se deben modificar las funciones
process y processReplacing para que respondan adecuadamente a los diferentes valores
de los parmetros. El algoritmo es simple:
si se est en el ciclo silenciado (bMuting ==true), escribe un cero en la siguiente
celda de salida
incrementa sampleCount
si sampleCount excede fMute * 10,000, reinicia sampleCount y establece bMuting
==false
si se est en un ciclo no-silenciado (bMuting ==false), escribe el valor del buffer
de entrada en la siguiente celda de salida
incrementa simpleCount
si sampleCount excede fPause * 10,000, reinicia sampleCount y establece
bMuting ==true

En la funcin processReplacing se puede ver esto claramente:

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AGapper : : pr ocessRepl aci ng ( f l oat **i nput s, f l oat **out put s,
l ong sampl eFr ames)
{
f l oat *i n1 = i nput s[ 0] ;
f l oat *i n2 = i nput s[ 1] ;
f l oat *out 1 = out put s[ 0] ;
f l oat *out 2 = out put s[ 1] ;
whi l e ( - - sampl eFr ames >= 0)
{
i f ( bMut i ng) {
( *out 1++) = 0 * ( *i n1++) ;
( *out 2++) = 0 * ( *i n2++) ;
++sampl eCount ;
i f ( sampl eCount > 10000 * get Par amet er ( 0) ) {
sampl eCount = 0;
bMut i ng = f al se;
}
} el se {
( *out 1++) = ( *i n1++) ;
( *out 2++) = ( *i n2++) ;
++sampl eCount ;
i f ( sampl eCount > 10000 * get Par amet er ( 1) ) {
sampl eCount = 0;
bMut i ng = t r ue;
}
}
78
}
}

La funcin process es casi idntica. Cabe aclarar el porqu se multiplica (*in1++) por
cero en el ciclo silenciado, es porque se debe incrementar el nmero de buffer de entrada,
an cuando el buffer de salida va a recibir un cero; entonces, con esta instruccin se estn
realizando ambas tareas.

En la Figura 6.5 se muestra cmo se ve el AGapper con la GUI preestablecida por el host
Cubase, posteriormente se modificar la GUI para afectarla como se desee.


Fig. 6.5 GUI Preestablecida en Cubase del Proyecto AGapper.

La GUI preestablecida a pesar de ser siempre bsica y limitada, le da la opcin al usuario
de configurar todos los parmetros programados; adems de que despliega el nombre y
unidades de los parmetros que fueron especificadas en el cdigo.

6.3.6 Modificando la GUI Parte I: VSTGUI
La manera ms sencilla de realizar una interfaz de usuario para el VST es utilizando
imgenes base de trabajos ya realizados. El trabajo para personalizar la UI ya ha sido
realizado, en la forma de la librera VSTGUI.

Cuando se desarrollan plugins con pocos parmetros, a veces no es del todo necesario el
crear UIs complejas, pues slo se estar desplegando un valor, por lo que se puede
utilizar la UI reestablecida del host. Sin embargo, hay veces en la que una perilla o slider
genrico no es suficiente, porque el host puede presentar la informacin de cualquier
manera, incluso como una simple pantalla negra con texto en ella; por lo que es
recomendable definir una UI que cumpla con su tarea.

El VSTGUI ofrece muchas clases de control a utilizar, los cuales se listan a continuacin:
CControl
CControlListener
COnOffButton
CParamDisplay
CTextEdit
COptionMenu
COptionMenuScheme
79
CKnob
CAnimKnob
CVerticalSwitch
CHorizontalSwitch
CRockerSwitch
CMovieBitmap
CMovieButton
CAutoAnimation
CSlider
CVerticalSlider
CHorizontalSlider
CSpecialDigit
CKickButton
CSplashScreen
CVuMeter
CFileSelector

Existen unos controles determinados que son ms tiles y son utilizados con mayor
frecuencia, como son: COnOffButton, CTextEdit, CKnob, CSlider, CParamDisplay, etc.
los cuales son implementados dentro de las clases en el cdigo a desarrollar. Las clases
en la librera VSTGUI pueden ser ordenadas como un rbol, que debe tomarse en cuenta
y familiarizarse con l antes de empezar a programar:


Fig. 6.6 rbol de Controladores de Librera VSTGUI [18].

Se debe notar que todas estas clases vienen de la clase original CView. Los controles que
se utilizarn vienen de la clase CControl, que viene de CView. Debido a que se puede
seguir desarrollando el rbol, el programador es libre de extender un controlador, de ser
necesario.

Hasta ahora, ya se cuenta con una serie de controles prefabricados con un
comportamiento comn. Para utilizarlos, la clase base AudioEffectX inicializa un editor
preestablecido que bsicamente le dice al host que haga lo que le corresponde. El
80
programador necesita crear su propio objeto del editor, extendiendo la clase base
AEffEditor y definiendo unas cuantas funciones y variables en ella. Las ms importantes
de esas variables sern los objetos derivados de CControl que se utilizarn desde la
VSTGUI.

6.3.7 Modificando la GUI Parte II: VSTGUI a Profundidad
Para poder desarrollar la interfaz grfica GUI del Gapper se deben tener cinco
componentes bsicos: un objeto CBitmap, dos objetos CVerticalSlider, y dos objetos
CParamDisplay. La clase CBitmap contiene el identificador de la fuente de un archivo
bitmap, y es utilizado para proveer la imagen de fondo de la interfaz. Los objetos
CVerticalSlider utiliza dos archivos bitmap, uno para la perilla del slider y una para el
fondo del slider. El programador debe proveer dichos bitmaps, ya sea hacindolos y
copiando los ejemplos del SDK. Finalmente, los objetos CParamDisplay sern utilizados
para desplegar el valor (en nmero reales) del slider, y cambiar conforme el slider es
desplazado.

Como ya se vio, lo que se debe hacer para implementar la interfaz es declarar y definir
una clase de editor y el editor de punto (miembro de AudioEffectX) a un objeto de esa
clase. Se debe revisar el archivo de encabezado de la clase de editor, AGEditor:

#i ncl ude " vst gui . h"

cl ass AGEdi t or : publ i c AEf f GUI Edi t or , publ i c CCont r ol Li st ener
{
publ i c:
AGEdi t or ( Audi oEf f ect *ef f ect ) ;
vi r t ual ~AGEdi t or ( ) ;
vi r t ual l ong open ( voi d *pt r ) ;
vi r t ual voi d cl ose ( ) ;
vi r t ual voi d set Par amet er ( l ong i ndex, f l oat val ue) ;
vi r t ual voi d val ueChanged ( CDr awCont ext * cont ext , CCont r ol *
cont r ol ) ;
pr i vat e:
/ / Cont r ol s
CVer t i cal Sl i der *mut eFader ;
CVer t i cal Sl i der *pauseFader ;
CPar amDi spl ay *mut eDi spl ay;
CPar amDi spl ay *pauseDi spl ay;

/ / Bi t map ( f or our backgr ound)
CBi t map *hBackgr ound;

/ / Ot her s
bool bOpened;
};

Aqu se tienen las declaraciones ya utilizadas de constructor y destructor, se han
declarado una funcin de abrir y cerrar, dos funciones de mantenimiento, dos sliders,
dos displays, y la imagen de fondo. Adems, se declar una funcin booleana, bOpened.
Posteriormente, se analiza el archivo AGEditor.cpp, donde trabajan todas las funciones
anteriores. Primero, se enumeran algunos factores:
81

enum{
/ / posi t i ons
kFader X = 78,
kFader Y = 100,
kFader I nc = 18,
kDi spl ayX = 70,
kDi spl ayY = 274,
kDi spl ayXWi dt h = 30,
kDi spl ayHei ght = 14
};

En esta parte se dibujan dos sliders (o faders, como se les llam en el cdigo), kFaderX y
kFaderY especifican la posicin de la esquina superior izquierda de un rectngulo donde
se colocan los sliders. kFaderInc representa la distancia horizontal entre los sliders. Se
especifican dos ventanas de display con dimensiones de 30 por 14, dentro de un
rectngulo ubicado en 70, 274.

Posteriormente, se declaran algunos colores para la GUI:

CCol or kLi ght Gr ay = {204, 204, 204, 0};
CCol or kMedGr ay = {238, 238, 238, 0};
CCol or kDar kGr ay = {51, 51, 51, 0};

CColor es una clase definida en vstgui.h y es utilizada slo para declarar el color.
Cuando se crea la ventana de display, por ejemplo, la clase CParamDisplay tiene unas
cuantas funciones que permiten definir el color de las letras, el color del fondo, y el color
del marco. Estas funciones esperan recibir un objeto CColor como su nico parmetro,
entonces slo se definen un par de grises a utilizar.


Fig. 6.7 GUI de AGapper Programada.

Como se puede ver en la Figura 6.7 se utiliz kDarkGray como el color de las letras,
kMedGray como el color del marco, y kLightGray como color de fondo. El VSTGUI
82
provee una serie de colores predefinidos que se pueden utilizar si uno lo desea, como son:
kTransparentCColor, kBlackCColor, kWhiteCColor, kGreyCColor, kRedCColor,
kGreenCColor, kBlueCColor, kYellowCColor, kCyanCColor y kMagentaCColor.
Despus se define una funcin para convertir un nmero flotante (en el rango de 0.0 a
1.0) a un entero (en el rango de 0 a 10,000) para representar el nmero de muestras que
se estn silenciando o pausando. Esto ya se hizo previamente, pero ahora se hace para la
clase del editor:

voi d f l oat ToSampl es ( f l oat val ue, char * st r i ng)
{
spr i nt f ( st r i ng, " %d" , ( i nt ) ( 10000 * val ue) ) ;
}

Para la implementacin del AGEditor es recomendable revisar el archive de encabezado
para recordar las definiciones que son necesarias hacer. Primero, el constructor:

AGEdi t or : : AGEdi t or ( Audi oEf f ect *ef f ect )
: AEf f GUI Edi t or ( ef f ect )
{
bOpened = f al se;
mut eFader = 0;
pauseFader = 0;
mut eDi spl ay = 0;
pauseDi spl ay = 0;

/ / l oad t he backgr ound bi t map
/ / we dont need t o l oad al l bi t maps, t hi s coul d be done
when open i s cal l ed
hBackgr ound = new CBi t map ( I DB_BI TMAP2) ;

/ / i ni t t he si ze of t he pl ugi n
r ect . l ef t = 0;
r ect . t op = 0;
r ect . r i ght = ( shor t ) hBackgr ound- >get Wi dt h ( ) ;
r ect . bot t om= ( shor t ) hBackgr ound- >get Hei ght ( ) ;
}

Se puede observar que el constructor requiere de un objeto AudioEffect en su creacin. El
AudioEffect es la clase superior de AudioEffectX, por lo que se debe pasar un apuntador al
objeto del efecto. Es necesario ver cmo el objeto AGEditor es creado en AGapper.cpp:

edi t or = new AGEdi t or ( t hi s) ;

Este llamado toma lugar dentro del constructor del AGapper y es el nico cambio que se
hace en l, aunque es recomendable intentar compilar sin errores para seguir adelante.
Entonces, el apuntador this hace referencia al objeto AGapper que crea el objeto
AGEditor, lo que es importante porque AGEditor necesita hacer una referencia de
regreso al plugin.

En el constructor: se deben apuntar los objetos CVerticalSlider y CParamDisplay a
NULL, cargar la imagen de fondo, y definir las dimensiones del plugin. La clase
83
CBitmap toma un identificador de referencia como parmetro, y lo provee a las funciones
getWidth() y getHeight() para recibir el dato de sus dimensiones. Las cuales son
utilizadas para definir las dimensiones efectivamente.

Este archivo de proyecto se puede utilizar como texto base para la creacin de otras
GUIs, al crear otra imagen de fondo y el programa se acoplar automticamente a las
dimensiones del bitmap. Tambin se define bOpened como falso, para indicar que el
editor no ha sido abierto an.

El valor recto para AGEditor es un miembro de AEffGUIEditor que se extendi, entonces
se recibe como derivado de una funcin superior. Posteriormente, se analiza el destructor,
el cual tiene una funcin importante:

AGEdi t or : : ~AGEdi t or ( )
{
/ / f r ee t he backgr ound bi t map
i f ( hBackgr ound)
hBackgr ound- >f or get ( ) ;
hBackgr ound = 0;
}

Todo lo que hace es llamar a forget(), la cual destruir el bitmap en caso de que se dejen
de hacer referencias a l. Ahora se debe discutir la funcin abrir que es llamada por el
host cuando es creado el VST. A continuacin se enlista lo que se debe hacer primero
para que el leer la funcin no sea un problema:
llamar la funcin abrir de la clase superior (AEffGUIEditor)
cargar los bitmaps necesarios
crear un CFrame y establecer una imagen de fondo al bitmap de fondo
crear e inicializar los objetos CControl que se necesitan, y desplegarlos
definir bOpened como cierto

Se debe notar que el objeto CFrame es slo un marco de nivel superior en el cual se
agregan todos los controles y bitmaps, que requiere que se le enve un CRect con las
dimensiones (segn las dimensiones del bitmap de fondo), un apuntador a la ventana de
un nivel superior del plugin (la cual enva el host cuando llama a la funcin abrir), y un
apuntador al editor al cual pertenece (en este caso, y en la mayora de los casos,
simplemente es el apuntador this), como se puede ver a continuacin:

l ong AGEdi t or : : open ( voi d *pt r )
{
/ / ! ! ! al ways cal l t hi s ! ! !
AEf f GUI Edi t or : : open ( pt r ) ;

/ / l oad some bi t maps
CBi t map* hFader Body = new CBi t map ( I DB_BI TMAP3) ;
CBi t map* hFader Handl e = new CBi t map ( I DB_BI TMAP1) ;

/ / - - i ni t backgr ound f r ame- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CRect si ze ( 0, 0, hBackgr ound- >get Wi dt h ( ) , hBackgr ound-
>get Hei ght ( ) ) ;
84
f r ame = new CFr ame ( si ze, pt r , t hi s) ;
f r ame- >set Backgr ound ( hBackgr ound) ;

/ / - - i ni t t he f ader s- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
i nt mi nPos = kFader Y;
i nt maxPos = kFader Y + hFader Body- >get Hei ght ( ) -
hFader Handl e- >get Hei ght ( ) - 1;
CPoi nt poi nt ( 0, 0) ;
CPoi nt of f set ( 1, 0) ;

/ / Mut e
si ze ( kFader X, kFader Y,
kFader X + hFader Body- >get Wi dt h ( ) , kFader Y +
hFader Body- >get Hei ght ( ) ) ;
mut eFader = new CVer t i cal Sl i der ( si ze, t hi s, kMut e, mi nPos,
maxPos, hFader Handl e, hFader Body, poi nt ) ;
mut eFader - >set Of f set Handl e ( of f set ) ;
mut eFader - >set Val ue ( ef f ect - >get Par amet er ( kMut e) ) ;
f r ame- >addVi ew ( mut eFader ) ;

/ / Pause
si ze. of f set ( kFader I nc + hFader Body- >get Wi dt h ( ) , 0) ;
pauseFader = new CVer t i cal Sl i der ( si ze, t hi s, kPause,
mi nPos, maxPos, hFader Handl e, hFader Body, poi nt ) ;
pauseFader - >set Of f set Handl e ( of f set ) ;
pauseFader - >set Val ue ( ef f ect - >get Par amet er ( kPause) ) ;
f r ame- >addVi ew ( pauseFader ) ;

/ / i ni t t he di spl ay
/ / Mut e
si ze ( kDi spl ayX, kDi spl ayY,
kDi spl ayX + kDi spl ayXWi dt h, kDi spl ayY +
kDi spl ayHei ght ) ;
mut eDi spl ay = new CPar amDi spl ay ( si ze, 0, kCent er Text ) ;
mut eDi spl ay- >set Font ( kNor mal Font Smal l ) ;
mut eDi spl ay- >set Font Col or ( kDar kGr ay) ;
mut eDi spl ay- >set BackCol or ( kLi ght Gr ay) ;
mut eDi spl ay- >set Fr ameCol or ( kMedGr ay) ;
mut eDi spl ay- >set Val ue ( ef f ect - >get Par amet er ( kMut e) ) ;
mut eDi spl ay- >set St r i ngConver t ( f l oat ToSampl es) ;
f r ame- >addVi ew ( mut eDi spl ay) ;

/ / Pause
si ze. of f set ( kFader I nc + hFader Body- >get Wi dt h ( ) , 0) ;
pauseDi spl ay = new CPar amDi spl ay ( si ze, 0, kCent er Text ) ;
pauseDi spl ay- >set Font ( kNor mal Font Smal l ) ;
pauseDi spl ay- >set Font Col or ( kDar kGr ay) ;
pauseDi spl ay- >set BackCol or ( kLi ght Gr ay) ;
pauseDi spl ay- >set Fr ameCol or ( kMedGr ay) ;
pauseDi spl ay- >set Val ue ( ef f ect - >get Par amet er ( kPause) ) ;
pauseDi spl ay- >set St r i ngConver t ( f l oat ToSampl es) ;
f r ame- >addVi ew ( pauseDi spl ay) ;

hFader Body- >f or get ( ) ;
hFader Handl e- >f or get ( ) ;

bOpened = t r ue;
85

r et ur n t r ue;
}

Para complementar est funcin se debe agregar la funcin cerrar, la cual es invocada
por el host cuando el plugin es retirado. Slo se debe limpiar todo al definir bOpened
como falso y borrando el CFrame:

voi d AGEdi t or : : cl ose ( )
{
bOpened = f al se;

del et e f r ame;
f r ame = 0;
}

Finalmente, se debe proveer una funcin para establecer el valor de un parmetro, y una
para poder manejar un cambio en valor de un parmetro, como se muestra a
continuacin:

voi d AGEdi t or : : set Par amet er ( l ong i ndex, f l oat val ue)
{
i f ( ! bOpened)
r et ur n;
swi t ch ( i ndex)
{
case kMut e:
i f ( mut eFader )
mut eFader - >set Val ue ( ef f ect - >get Par amet er
( i ndex) ) ;
i f ( mut eDi spl ay) {
mut eDi spl ay- >set Val ue ( ef f ect - >get Par amet er
( i ndex) ) ;
}
br eak;
case kPause:
i f ( pauseFader )
pauseFader - >set Val ue ( ef f ect - >get Par amet er
( i ndex) ) ;
i f ( pauseDi spl ay) {
pauseDi spl ay- >set Val ue ( ef f ect - >get Par amet er
( i ndex) ) ;
}
br eak;
}
post Updat e ( ) ;
}

/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
voi d AGEdi t or : : val ueChanged ( CDr awCont ext * cont ext , CCont r ol *
cont r ol )
{
l ong t ag = cont r ol - >get Tag ( ) ;
swi t ch ( t ag)
{
86
case kMut e:
case kPause:
ef f ect - >set Par amet er Aut omat ed ( t ag, cont r ol - >get Val ue
( ) ) ;
cont r ol - >updat e ( cont ext ) ;
br eak;
}
}

Con esta ltima parte del cdigo se tiene definida la clase AGEditor, la cual para ser
utilizada se debe apuntar el AGapperEditor a un objeto de esta clase, y el resto es
manipulado sin esfuerzo. Finalmente, se debe actualizar la funcin
AGapper::setParameter para hacer uso de la funcin AGapperEditor del mismo nombre:

voi d AGapper : : set Par amet er ( l ong i ndex, f l oat val ue)
{

i f ( i ndex == 0)
f Mut e = val ue;
el se i f ( i ndex == 1)
f Pause = val ue;

( ( AEf f GUI Edi t or *) edi t or ) - >set Par amet er ( i ndex, val ue) ;
}

6.3.8 Eventos MIDI en el VST
Para la creacin de instrumentos, lo que se debe hacer es tomar lo que se ha realizado
hasta ahora y darle a conocer al host que no se estar trabajando con un buffer con datos
muestreados de audio, sino que se espera recibir datos MIDI, y se utilizarn esos datos
para determinar lo que ir hacia los buffers de salida.

Es ms difcil de lo que parece, pues es muy diferente tomar datos y afectarlos
matemticamente a construir ondas sonoras a partir de la nada. Sin embargo, se pueden
realizar tareas un poco menos complicadas; por ejemplo, se puede construir una onda con
dientes de cierra sabiendo la frecuencia y la amplitud, simplemente escribiendo nmeros
cada vez ms grandes en cada buffer de salida hasta que se alcanza el pico, y bajando la
amplitud hasta cero de repente. El algoritmo sera algo como:

def i ne f r eq; / / t he amount of t i me ( i n sampl es) i t t akes t o
compl et e a cycl e
def i ne amp; / / t he hei ght ( bet ween 0 and 1) at whi ch t he
wave peaks
val ue = 0;
whi l e ( ) {
val ue += ( amp/ f r eq) ;
out put ++ = val ue;
i f ( val ue > amp) val ue = 0;
}

Esta parte del cdigo no toma en cuenta los cambios de pitch, pero eso requiere de un
clculo extra. Por ahora, es importante obtener sonido desde el instrumento, y responder a
87
los datos de la nota. De hecho, ajustar la frecuencia ajustar el pitch, entonces se estar
bastante cerca de lo que se debe hacer para desarrollar un verdadero instrumento cuando
se necesite [18].

Sin embargo, al generar sonido a partir del VST, crea un VSTi (Instrumento VST) el cual
tiene un enfoque diferente, pues ya no se recurre a la lectura de los buffers esperando
recibir audio, sino que se busca una entrada digitalizada de MIDI. En este proyecto se
recurri a analizar la respuesta a eventos MIDI, leyendo los datos recibidos en el buffer y
utilizndolos para control de audio.

En los eventos MIDI son una subdivisin de un evento de mayor tamao llamado VST
Events, los cuales se representan como estructuras. Un sintetizador o cualquier otro
instrumento virtual necesitan indicar que requiere de eventos VST para funcionar
adecuadamente. Esto se realiza con el llamado al evento isSynth en el constructor que
indica la peticin de eventos. De manera adicional, se llama a otro mtodo con el nombre
wantEvents() en un mtodo del sintetizador llamado resume(), la cual es llamada cada vez
que se activa el sintetizador. Se debe notar que en el constructor se suspende el
sintetizador, de manera que el host necesita activarlo y la funcin wantEvents es llamada
de la siguiente manera:

voi d Synt h: : r esume ( )
{
want Event s ( ) ;
}

Los eventos que manda a llamar el plugin son controlados por un mtodo llamado
processEvents, que maneja un parmetro que es un apuntador a la estructura VSTEvent y
el valor regresado indica si el plugin necesita ms eventos. Los eventos MIDI se
identifican por un valor kVstMidiType de la variable de miembro del tipo en la estructura.
Despus de la identificacin de tipo, la estructura puede ser enviada a un VstMidiEvent.

Vale la pena analizar la forma en que los instrumentos controlan los eventos de una nota
o evento MIDI. Los controladores que tienen un nmero de siete bits y un valor pueden
alterar un parmetro del sintetizador mientras ste funciona. Usualmente, los
controladores en hardware de los sintetizadores envan mensajes MIDI de controladores:
los datos contienen el nmero de la nota (entonacin) y la velocidad. Como los
instrumentos MIDI pueden ser tocados en tiempo real, la longitud de la nota no puede ser
conocida con anticipacin, aunque as es como trabajan los secuenciadores realmente, y
entonces los eventos de NOTEOFF (o NOTEONs con velocidad cero) pueden ser
enviados para detener el sonido de las notas.

l ong Synt h: : pr ocessEvent s ( Vst Event s* ev)
{
f or ( l ong i = 0; i < ev- >numEvent s; i ++)
{
i f ( ( ev- >event s[ i ] ) - >t ype ! = kVst Mi di Type)
cont i nue; / / I gnor e!
Vst Mi di Event * event = ( Vst Mi di Event *) ev- >event s[ i ] ;
char * mi di Dat a = event - >mi di Dat a;
88
l ong st at us = mi di Dat a[ 0] & CHANNEL_MASK; / / i gnor i ng
channel
i f ( st at us == NOTE_ON | | st at us == NOTE_OFF) / / we
onl y l ook at not es
{ / / Handl e MI DI not e event s.
l ong not e = mi di Dat a[ 1] & MAX_MI DI _VALUE;
l ong vel oci t y = mi di Dat a[ 2] & MAX_MI DI _VALUE;
i f ( st at us == NOTE_OFF)
vel oci t y = 0; / / not e of f by vel oci t y 0
i f ( vel oci t y == 0 && ( not e == cur r ent Not e) )
not eOf f ( ) ;
el se
not eOn ( not e, vel oci t y, event -
>del t aFr ames) ;
}
el se i f ( st at us = CONTROLLER)
{ / / Handl e MI DI cont r ol l er event s.
char number = mi di Dat a[ 1] & MAX_MI DI _VALUE;
f l oat val ue = ( f l oat ) ( ( mi di Dat a[ 2] &
MAX_MI DI _VALUE) * mi di Scal er ) ;
cont r ol l er Changed( number , val ue) ;
} / / el se i f
event ++;
} / / f or
r et ur n 1; / / want mor e
}

Las funciones NOTEON y NOTEOFF administran el manejo de las notas, y
controllerChanged responde a los eventos del controlador. Los tres mtodos se expresan
de la siguiente manera:

voi d Synt h: : not eOn ( l ong not e, l ong vel oci t y, l ong del t a)
{
cur r ent Not e = not e;
cur r ent Vel oci t y = vel oci t y;
cur r ent Del t a = del t a;
not eI sOn = t r ue;
f Phase1 = f Phase2 = 0;
}

voi d Synt h: : not eOf f ( )
{
not eI sOn = f al se;
}

voi d Synt h: : cont r ol l er Changed( i nt number , f l oat val ue)
{
swi t ch( number )
{ / / Shoul d be sel f - expl anat or y.
case CC_MODULATI ON: f Lf oDept h = val ue; r et ur n;
case CC_EMPHASI S: f Fi l t er Q = val ue; br eak;
case CC_BRI GHTNESS: f Fi l t er Fr eq = val ue; br eak;
} / / swi t ch
/ / Thi s must be cal l ed t o t el l t he host t hat par ams have
changed
/ / and t he UI woul d need updat i ng.
89
updat eDi spl ay( ) ;
}

El objetivo es que las variables de los miembros del sintetizador que afectan el
procesamiento de audio son cambiados en los mtodos en respuesta a los eventos MIDI.
En el mtodo del controlador la funcin updateDisplay() es un mtodo plug-to-host que
notifica al host que los parmetros han cambiado sin que el usuario haya modificado los
controles de la UI, entonces la UI necesita actualizarse segn se requiera.

A diferencia del caso en que se trabaja con audio, el programador no necesita establecer
el nmero de entradas o salidas MIDI que tiene el sintetizador, pues el nmero es
provisto por la cantidad de eventos MIDI que el sintetizador procesa. Tambin es posible
generar nuevos eventos MIDI y enviarlos al host llamando la funcin
sendVstEventsToHost. Sin embargo, esta funcin es poco utilizada, pues el sintetizador
responde a los eventos MIDI desde un principio y los eventos son generados
automticamente.

90