Date post: | 01-Jan-2016 |
Category: |
Documents |
Upload: | luis-alejandro-giraldo |
View: | 23 times |
Download: | 0 times |
Proyecto OpenPipe: Diseño Conceptual de un Controlador MIDI
09 de Abril de 2007 (primera versión pública, por completar)
http://openpipe.wordpress.com/
2007 © Santiago José Barro Torres <[email protected]>
Estudiante de Quinto curso de Ingeniería Informática
Facultade de Informática
Universidade da Coruña
Objetivo del Documento
En ingeniería de sistemas, el diseño conceptual siempre precede al diseño
físico y a la implementación. El diseño conceptual pretende dar una visión de
alto nivel del sistema que se va a construir, de forma simbólica. Es de gran
ayuda para su comprensión y asimilación por parte de los ingenieros y los
usuarios, y también permite el establecimiento de pautas y metodologías
dentro del diseño de sistemas, dotando de orden y sentido a ésta actividad.
Un diseño conceptual es un modelo construido sobre papel, a un nivel alto de
abstracción. A su vez, un modelo es una abstracción de una parte de la
realidad, un pequeño minimundo (en nuestro caso, un sistema informático) que
tratamos de describir formalmente. Abstracción significa discernir entre
importante y no importante, relevante e irrelevante; por lo que es, en última
instancia, decisión del ingeniero el escoger la granularidad o nivel de detalle
con el que se va a describir el modelo. Es decir, la pregunta principal es: En mi
nivel de abstracción, ¿qué es importante para mí? ¿Qué detalles puedo obviar?
Sin embargo, habitualmente no basta con un único modelo para describir un
sistema:
Un diseño físico es “práctico”, en el sentido que es el único imprescindible para
iniciar la construcción e implementación del sistema. Sin embargo, el diseño
físico está atado a la tecnología predominante en un instante de tiempo; por lo
que un cambio en la tecnología significa un cambio en el diseño físico. Por el
contrario, el diseño conceptual a partir del cual debería haber surgido el diseño
físico se mantiene, en un caso ideal, prácticamente constante. Además, un
diseño físico es un modelo a un nivel demasiado bajo, en el sentido de que es
mucho más complicado encontrar los errores conceptuales (o de alto nivel) que
se producen inherentemente durante el proceso de diseño.
El diseño conceptual es un primer paso fundamental en el diseño de un
sistema. Pero aún así, el diseño conceptual sigue siendo insuficiente, ya que no
proporciona los detalles suficientes como para construir el sistema real.
Necesitamos un diseño físico, concreto, que nos diga cómo implementar el
sistema a partir de cada uno de sus subsistemas; o que al menos proporcione
directrices generales que puedan servir de guía.
Como conclusión sacamos que ni el diseño conceptual ni el diseño físico son,
en general, suficientes por sí solos.
El Proyecto OpenPipe y el Diseño Conceptual
El presente documento forma parte del proyecto OpenPipe, cuyo objetivo
principal es el diseño e implementación de un controlador MIDI con apariencia
de una flauta o de una gaita.
Esquema Conceptual de la OpenPipe
Como veremos, OpenPipe no es un dispositivo excesivamente complejo. La
mayor parte de su funcionalidad la realizará un microcontrolador (situado
dentro de la Unidad de Procesamiento), que no es más que un circuito
integrado con capacidad de control (ejecución de programas). Como
consecuencia, el diseño conceptual estará centrado en definir la estructura de
cada uno de los componentes y su interconexión con el microcontrolador, que
será el encargado de traducir la información musical obtenida del cilindro con
“sensores” a información MIDI interpretable por otro dispositivo externo
conectado a la interfaz de salida MIDI (un ordenador, un sintetizador, un
secuenciador...).
El principal problema de un diseño “hardware”, como es el caso de OpenPipe,
es que determinados componentes pueden ser difíciles de encontrar, ya sea
por estar anticuados, o porque la empresa que los fabricaba ha decidido
retirarlos de su catálogo. Ello no significa que no se pueda implementar la
OpenPipe, ya que posiblemente existan componentes alternativos de ese u
otro fabricante perfectamente válidos. Lo que sucede es que probablemente
habrá que modificar ligeramente el diseño físico, exigiendo parte de
conocimientos por parte de la persona que finalmente decide implementar la
OpenPipe.
Un segundo problema surge a raíz de su naturaleza OpenSource. Los
microcontroladores son dispositivos muy variados, y habitualmente cada
familia tiene su propio software de programación (ensambladores, enlazadores,
compiladores...) creados por sus propios fabricantes o terceros, que conocen la
implementación del microcontrolador. Esa implementación no es pública, por lo
que es complicado que exista software libre para la programación del
microcontrolador que nosotros hemos elegido (existe software libre de
programción para los modelos de microcontroladores más populares, como
pueden ser determinadas familias de los PIC, o algunos modelos “clásicos”,
como el 8051, por ejemplo).
Es más, cuando alguien compra una tarjeta de evaluación (una tarjeta en la
que se puede programar y probar el microcontrolador), es habitual que el
fabricante ya incluya software de programación. Lo cual complica tener un
único diseño físico para OpenPipe, pues un programa en ensamblador para una
familia de microcontroladores generalmente no es reutilizable directamente
para otra familia de microcontroladores. ¡El código en ensamblador no se
caracteriza precisamente por ser portable! ¡Todo lo contrario! Es específico
para cada familia de microcontroladores, o en el peor de los casos,
dependiente incluso de un modelo particular de una familia de
microcontroladores.
Los dos problemas anteriores recalcan la importancia de tener un diseño
conceptual para la OpenPipe. Quien quiera implementar la OpenPipe, tendrá
que formalizar el diseño físico adecuado para su caso. Probablemente, cada
ingeniero tendrá sus preferencias respecto a microcontroladores. Mi tutor [1]
es un amante de la familia 8051, en cambio hay muchos estudiantes que están
más aficionados a los PIC.
¡Que cada uno escoja el que más le guste! Sin embargo, el modelo conceptual
seguirá siendo válido, en todo caso. En caso de que otras personas hagan
implementaciones para otros microcontroladores, se publicarían en la web del
proyecto http://openpipe.wordpress.com/.
De todas formas, aunque es un diseño conceptual, se han incluído pautas de
diseño que, de alguna forma, lo complementan.
Definición de Controlador MIDI
En pocas palabras, y definido de forma sencilla, un controlador MIDI no es más
que un dispositivo lógico (implementado en software) o un dispositivo físico
(implementado en hardware) que produce mensajes MIDI a partir de la
ocurrencia de una serie de eventos producidos por la interacción con un
usuario.
¿Qué es un evento? Un evento no es más que un determinado hecho que ha
ocurrido o que ocurrirá en un determinado instante del tiempo. Normalmente,
para nosotros (como programadores) la ocurrencia de un evento tendrá
asociada la ejecución de un determinada acción.
Por ejemplo, para una aplicación visual (es decir, con interfaz gráfica), que el
usuario haga clic sobre un determinado botón puede significar un evento.
Probablemente, cuando ocurra el evento “el usuario ha hecho clic sobre el
botón X”, desearemos ejecutar una determinada porción de nuestro código.
Por tanto, en la programación visual es bastante normal programar de forma
orientada a eventos.
El concepto de “evento” es intencionadamente poco concreto, puesto que en
principio, cualquier hecho puede ser potencialmente un evento. ¿Cuál es la
frontera entre un hecho y un evento? Como hemos comentado anteriormente,
un evento tiene un significado especial para nosotros, pues estamos
interesados en saber en qué momento tiene lugar su ocurrencia, para actuar
en consecuencia. Es el propio programador quien define qué eventos quiere
controlar, y como cabría esperar, son específicos para cada aplicación.
En el caso de un controlador MIDI, un evento es cualquier tipo de hecho
relacionado con la interpretación de un músico, cuya ocurrencia sea relevante
para la posterior ejecución del sonido por parte de un sintetizador. Por ejemplo,
si deseamos crear un controlador MIDI con apariencia de piano, un posible
evento sería la pulsación por parte del pianista de una tecla, ya que
posiblemente querremos iniciar la ejecución de una nota cuando el pianista
pulse una tecla. Como cabría esperar, que un pianista deje de pulsar una tecla
también podría ser un evento, éste asociado con la finalización de la ejecución
de la nota anteriormente iniciada.
Sin embargo, mientras tengamos medios para detectar un evento, podemos
definir el número que nosotros queramos. La idea es determinar qué eventos
son importantes de cara a simular el comportamiento de un instrumento real,
lo cual depende del instrumento en cuestión. Por ejemplo, en el modelo del
piano, sería interesante que la intensidad del sonido fuese proporcional a la
intensidad con que el pianista ha pulsado la tecla.
Lo anterior no sería válido si en vez de diseñar un controlador para un piano,
quisiésemos diseñar un controlador para un violín o para una flauta. Para el
caso de un controlador MIDI con apariencia de violín, estaríamos interesados
en saber en qué momento el músico frota y deja de frotar una cuerda con el
arco, lo cual constituirían sendos eventos. Para el caso de un controlador MIDI
con apariencia de flauta, estaríamos interesados en saber qué agujeros está
tapando y destapando en un determinado instante, lo cual constituirían el
evento principal.
De todas formas, aunque los eventos que estamos monitorizando en cada caso
sean completamente distintos entre sí (¿verdad que no es lo mismo pulsar una
tecla que frotar una cuerda?), podemos seguir hablando de evento como un
hecho abstracto. Al fin y al cabo, aunque sean eventos distintos, tanto al pulsar
una tecla en un piano como al frotar una cuerda en un violín implican acciones
similares: Inicio de ejecución de una nota.
Capturando Eventos
Ahora viene el problema, ¿cómo capturamos los eventos? En el caso de un
controlador software el problema es trivial, ya que son los propios lenguajes de
programación los encargados de capturar el evento. El programador no tiene
más que proporcionar una implementación para la función de callback que el
lenguaje de programación invocará cuando se produzca el evento deseado. Por
ejemplo, en el caso de Java:
JButton boton = new JButton(“Púlsame”);boton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {/* Aquí iría la acción asociada al evento */System.out.println(“¡Me han pulsado!”);
}});
En el caso de un controlador hardware, el problema es más complicado, ya que
necesitamos dispositivos electromecánicos que sean capaces de detectar el
evento deseado. En el ejemplo del piano, necesitamos que, de alguna forma, la
tecla nos proporcione información del estado de su pulsación por medio de
señales eléctricas. Si además queremos conocer la intensidad con que la tecla
ha sido pulsada, la tecla es quien ha de proveer dicha información mediante
otra señal eléctrica.
Para capturar eventos, podemos utilizar cualquier tipo de dispositivo que nos
imaginemos. La complejidad puede ser tan grande o tan pequeña como
nosotros queramos, siempre al alcance de nuestras posibilidades. No habría
ningún problema en utilizar pulsadores o interruptores convencionales. Ahora
bien, a la hora de interpretar melodías, ¡notaremos que nuestro diseño, aún
siendo correcto, es poco práctico!
Se recomienda consultar los catálogos de tiendas de electrónica por internet.
Hay bastantes posibilidades de encontrar sensores u otros dispositivos a partir
de los cuales podamos capturar los eventos que deseamos. ¡Es cuestión de
tener paciencia y afán de investigación!
Capturando eventos en OpenPipe
Como seguramente muchos ya sabréis, el objetivo del proyecto OpenPipe es
proporcionar el diseño de un controlador MIDI con apariencia de flauta o gaita.
Todo lo comentado hasta ahora también es válido para OpenPipe, pero
aplicado al caso particular de los flautistas o gaiteiros.
¿Cuál es el evento más importante a detectar en éste tipo de vientos? Pues sin
duda, es indispensable conocer qué agujeros están tapados o destapados en
un determinado instante. Grosso modo, el flautista toca diversas notas tapando
unos agujeros u otros. La flauta, como muchos otros instrumentos de viento,
tiene una serie de “posturas” asociadas para cada nota que puede tocar. El
conjunto de todas las “posturas” que puede tocar un instrumento constituye la
digitación.
Para el caso de la flauta, existen dos digitaciones alternativas: La digitación
alemana, y la digitación barroca. Con ambas digitaciones se pueden conseguir
el mismo número de notas, lo que sucede es que, en determinadas notas, la
“postura” asociada es distinta. Quien lo notará, principalmente, es el
intérprete. De cara al espectador que escucha, no habrá prácticamente
diferencia (¡a menos que sea tan ávido como para llevar cuenta de los agujeros
que tapa y destapa, lo cual personalmente me parece improbable!).
Durante algún tiempo estuve buscando por la red algún tipo de sensor que
fuese capaz de detectar la pulsación de un dedo, no de forma mecánica (no un
pulsador), sino de forma sensitiva. Mirándolo desde el punto de vista del
intérprete, lo más cómodo es que el “sensor” en cuestión cambie de estado
simplemente con posar el dedo encima, y nunca pulsando un elemento
mecánico, ya que resultaría en un aparato engorro de utilizar.
Después de una búsqueda incesante, sin ningún resultado positivo, decidí
pedirle ayuda al que será mi tutor en el proyecto fin de carrera [1]. Cuando
entendió lo que quería hacer (relativo al “sensor”), me propuso un circuito tan
sencillo como el siguiente:
Esquema propuesto del “sensor” que capturará los eventos.
Los que tengan conocimientos de electrónica, sabrán que es un circuito
bastante sencillo. No es más que un generador de tensión conectado a una
impedancia (o resistencia). La idea es la siguiente: Nuestro cuerpo también
genera internamente electricidad. Si tocamos con nuestra mano un punto
cualquiera del circuito mostrado en la figura, introducimos una pequeña
componente de corriente, de unos pocos miliamperios (la cantidad aproximada
habría que estimarla experimentalmente). Si además la resistencia es lo
suficientemente grande, la corriente que circula por el circuito es pequeña
(recordemos la fórmula de Ohm V = IR), por lo que llegará un momento en el
que la corriente introducida por una persona al tocar el circuito será lo
suficientemente significativa como para detectar cuándo se está tocando el
circuito, y cuándo no.
En definitiva, con los valores de tensión y resistencia adecuados, cuando una
persona toque el circuito, se producirá un aumento significativo en la tensión
debido principalmente a la corriente producida en el interior del cuerpo de la
persona. Ahora bien, dicho valor de tensión no es utilizable directamente, pues
contiene un valor impreciso. Suponiendo conocidos los umbrales de tensión
ideales (determinados de forma empírica) para detectar “pulsación” / “no
pulsación”, la señal debería pasar previamente por un comparador que,
teniendo en cuenta dichos umbrales, generaría finalmente los valores de
tensión correspondientes a los valores lógicos “alto” y “bajo” utilizados por el
resto de la circuitería (5 voltios y 0 voltios; o 3’3 voltios y 0 voltios; etc.).
Una vez que ya hemos definido cómo se construiría un “sensor”, podemos
juntar varios para hacer nuestro instrumento.
Esquema Conceptual de la OpenPipe
En la figura se puede observar que se han utilizado ocho “sensores”. Cada uno
de ellos se correspondería con un agujero en una flauta convencional. Se ha
añadido uno adicionalmente, para utilizarlo como “sensor” de control.
Lo más normal es que distribuyamos los nueve “sensores” anteriores sobre un
tubo cilíndrico, para imitar la forma de una flauta o una gaita. En principio, el
material del tubo cilíndrico podría ser plástico o incluso madera. Los sensores
estarían “incrustados” literalmente en el tubo, y no sería más que un pequeño
cilindro metálico en el que se introduciría la componente de corriente generada
por el cuerpo del intérprete.
Los “sensores” irían incrustados en el tubo.
Ahora bien, el resto de la circuitería correspondiente a cada uno de los
sensores estaría en la unidad de procesamiento, junto al microcontrolador y al
resto de componentes que conforman la lógica de OpenPipe.
Diseño de la Lógica del Controlador MIDI
Hasta ahora no se ha comentado nada relativo al procesamiento de datos. Nos
hemos limitado a detectar eventos, y a generar señales eléctricas (en principio
digitales, aunque también podrían ser analógicas) en respuesta a esos eventos.
Sin embargo, no hemos de olvidar que nuestro Controlador MIDI debe generar
mensajes MIDI como respuesta a los eventos que se han producido.
¿Por qué tipo de interfaz enviaremos la salida? Porque no es lo mismo enviar
datos a través del puerto USB que a través de una tarjeta de Red. Como cabría
esperar, a la salida tendremos una interfaz MIDI, que será la que transmita
nuestros datos a otro dispositivo MIDI externo, probablemente un sintetizador.
En caso de querer conectar OpenPipe al ordenador, es posible encontrar
interfaces MIDI – USB a partir de 50 euros en tiendas especializadas. También
existen opciones más económicas, pero más en desuso, como podría ser
interfaces MIDI – Puerto de Juegos, en caso de que la tarjeta de sonido
disponga de tal puerto.
En todo caso, a pesar de la gran cantidad de opciones de interconexión que
existen actualmente, es conveniente diseñar OpenPipe para que trabaje con
interfaces MIDI. La razón es muy sencilla: La gran mayoría de dispositivos
musicales electrónicos siguen utilizando el protocolo MIDI como estándar de
facto, aunque existan alternativas más modernas al respecto. En cualquier
caso, siempre queda la posibilidad de utilizar cables conversores y / o hardware
y software especializado para buscar la combinación deseada.
Entonces ya tenemos claro que nuestro diseño lógico: (a) Por un lado, tendrá
como entrada una serie de señales digitales que contienen información sobre
la ocurrencia de determinados eventos, y (b) Por otro lado, deberá producir
como salida información MIDI a través de una interfaz de dicho tipo.
Quienes hayan estado trabajando en temas relacionados con la electrónica,
sabrán que en cuanto es necesario “procesar” una pequeña cantidad de
información, se suele utilizar un componente denominado microcontrolador.
Microcontroladores y Lógica de Control
¿Qué es un microcontrolador? Pues a grandes rasgos, un microcontrolador es
como un microprocesador, en el sentido que tiene capacidad de procesamiento
(puede ejecutar instrucciones de un programa) pero más limitado, puesto que
normalmente se utilizan en actividades de control dentro de otros dispositivos,
o incluso en procesos industriales (donde su uso es más habitual).
Las actividades de control suelen ser rutinarias, y se caracterizan por necesitar
una cierta cantidad de procesamiento de datos a bajo nivel. Los
microcontroladores suelen incorporar una memoria interna de programa, en la
que se puede almacenar el programa de control, una memoria de datos, para
almacenar datos y variables, así como puertos de entrada / salida (tanto
analógicos como digitales) que permiten el flujo de datos con el exterior.
La arquitectura del microcontrolador es específica para cada aplicación.
Podemos encontrarnos microcontroladores con más o menos capacidad de
memoria, con más o menos puertos de entrada / salida. Incluso es habitual que
los microcontroladores incorporen algún tipo de periférico, como puede ser un
puerto serie, un puerto paralelo, un puerto USB, una tarjeta de red, etc. Todo
depende del uso que queramos darle.
En nuestro caso, hablaremos sobre el microcontrolador 8051. Se podrían haber
escogido muchos otros microcontroladores, siendo igualmente válida la
elección. Sin embargo, uno de los puntos a favor del 8051 es que es un
microcontrolador considerado actualmente como un “estándar”, muchos
fabricantes tienen en su catálogo diversos microcontroladores compatibles con
la familia 8051, como por ejemplo Phillips y Samsung.
La arquitectura “tradicional” del 8051 está formada por los siguientes
elementos:
● Arquitectura Harvard (existen espacios de direcciones separados para
código y datos).
● CPU de 8 bits.
● Procesador booleano (operaciones sobre bits).
● Permite direccionar hasta 64 KB de ROM externa y 64 KB de RAM.
● Dos contadores / temporizadores.
● 128 o 256 bytes de memoria RAM interna (16 bytes de los cuales son
direccionables a nivel de bit).
● 4 KB de memoria ROM interna.
● Cuatro puertos de entrada / salida de ocho bits.
● Una UART.
● Cinco fuentes de interrupción (dos externas, dos para los temporizadores
y una para el puerto serie).
● Oscilador interno.
Sin embargo, como se trata de un diseño conceptual (a alto nivel), no se
profundizará mucho más sobre la arquitectura concreta del microcontrolador.
Simplemente se esbozará cómo el microcontrolador es capaz de controlar la
lógica del funcionamiento de la OpenPipe.
Lógica de Funcionamiento de un Controlador MIDI
A partir de los nueve sensores indicados anteriormente, obtendremos nueve
señales binarias, de valor uno o cero. Podemos considerar que un valor de uno
es “agujero tapado”, y que un valor de cero es “agujero destapado”, aunque
sería sencillo implementar una lógica alternativa.
En principio, mientras el usuario mantiene una combinación de agujeros
tapados / destapados, se supone que el instrumento ha de mantener la nota
actual. En el momento que se destape o se tape un nuevo agujero, habrá que
silenciar la nota anterior (es decir, enviar un mensaje NOTE_OFF(notaId) de la
nota que estaba sonando hasta ese momento) y generar una nueva nota (es
decir, enviar un mensaje NOTE_ON(notaId) de la nota que se corresponda con
la nueva postura).
¿Cómo se podría implementar lo anterior? Pues con un bucle que comprobaría
continuamente el estado actual de los “sensores” (notar que el estado de los
sensores viene determinado por el valor de la señal eléctrica correspondiente)
con un estado de referencia (que se correspondería con la última nota
generada). En el momento que el estado actual no fuese exactamente igual al
estado de referencia, apagaríamos la nota actual y generaríamos la nueva
nota, puesto que un cambio en el estado de los “sensores” implica,
necesariamente, un cambio de postura por parte del usuario, lo cual implica, a
su vez, un cambio de nota.
Por tanto, es necesario tener almacenado en una serie de variables el valor
anterior de la señal, para ser capaces de detectar un cambio de estado en
cualquier instante de tiempo. La encuesta se haría de forma continua, ya que
el aparato tiene que ser capaz de responder en tiempo real a los eventos
producidos por parte del usuario.
001002003004005
006007008
// Encuesta el estado actual de los sensoresestadoAnteriorSensor0 = estadoSensor0 = detectarEstadoSensor0();estadoAnteriorSensor1 = estadoSensor1 = detectarEstadoSensor1();…estadoAnteriorSensor8 = estadoSensor8 = detectarEstadoSensor8();eventoNoteON(estadoSensor0, estadoSensor1, …, estadoSensor8);
while(1 + 1 != 7) {while(estadoSensor0 == estadoAnteriorSensor0 &&
estadoSensor1 == estadoAnteriorSensor1 && … &&
009
010011012013014
015
016
017018019020021
estadoSensor8 == estadoAnteriorSensor8) {
// Encuesta del estado actual de los sensoresestadoSensor0 = detectarEstadoSensor0();estadoSensor1 = detectarEstadoSensor1();…estadoSensor8 = detectarEstadoSensor8();
}
//// El siguiente código se ejecuta si, y sólo si, el estado// actual no es igual al estado anterior, lo que significa// que el usuario ha cambiado su postura//
// Silenciamos la nota anterioreventoNoteOFF(estadoAnteriorSensor0, estadoAnteriorSensor1, …,
estadoAnteriorSensor8);// Iniciamos la ejecución de la nueva notaeventoNoteON(estadoSensor0, estadoSensor1, …, estadoSensor8);
//// El estado actual pasa a ser el estado anterior//estadoAnteriorSensor0 = estadoSensor0;estadoAnteriorSensor1 = estadoSensor1;…estadoAnteriorSensor8 = estadoSensor8;
}
Figura: Programa de Control de OpenPipe
En la figura se ha escrito en pseudocódigo el programa de control de OpenPipe.
Las líneas de la 1 a la 4 detectan el estado actual de cada uno de los nueve
sensores (numerados desde el cero al ocho). La pseudofunción
detectarEstadoSensor0() detecta el estado del sensor 0, que estará conectado
a una de las señales de algún puerto de entrada salida. En la práctica, lo
habitual es consultar el estado de un bit de algún registro especial del
microcontrolador, como sucede en el 8051.
Se han utilizado nueve variables de un bit para almacenar el estado actual de
cada uno de los nueve sensores. El valor de dichas variables se actualizará de
forma constante mediante encuesta, como se puede comprobar en las líneas
de la 10 a la 13. Además, se utilizan otras nueve variables de un bit para
almacenar el estado anterior de cada uno de los nueve sensores, que también
se inicializan en las líneas 1 a 4. El valor de dichas variables se utiliza como
valores de referencia para detectar los cambios de postura por parte del
usuario.
Las líneas 7 a 14 mantienen el programa en espera activa mientras no se
produce un cambio de postura. En el momento que el estado actual no es el
mismo que el estado de referencia (entendiendo un estado como un valor
determinado por el conjunto de valores de los sensores en un instante
arbitrario), se pasa a ejecutar las instrucciones de las líneas 15 y 16, que
ejecutan dos pseudofunciones. La primera es
eventoNoteOFF(estadoAnteriorSensor0, estadoAnteriorSensor1, …,
estadoAnteriorSensor8), y envía un evento NoteOFF para la nota asociada con
el estado anterior de los sensores. La segunda es
eventoNoteON(estadoSensor0, estadoSensor1, …, estadoSensor8), que envía
un evento NoteON para la nota asociada con el estado actual de los sensores.
Finalmente, las líneas 17 a 20 establecen que el estado anterior es igual al
estado actual.
La implementación de las pseudofunciones eventoNoteOFF(estado) y
eventoNoteON(estado) se comentará posteriormente. En principio, nos basta
con saber que cada una de las funciones genera un mensaje NOTE_OFF y
NOTE_ON, respectivamente. Dicho mensaje se enviará por el puerto de salida
MIDI.
Estado de los “Sensores”, Posturas y Notas MIDI
Es interesante comentar la relación entre los siguientes conceptos: Estado de
los “Sensores”, Posturas y Notas MIDI.
En primer lugar, habíamos definido el estado de los “sensores” como una
combinación de los estados individuales de cada “sensor”. En términos
matemáticos, podríamos pensar como un vector de nueve elementos con
valores binarios representado estados “tapado” / “destapado”.
Por otra parte, habíamos comentado que una “postura” es una combinación de
dedos “tapando” y “destapando” agujeros, que es la que se utiliza en un
determinado instrumento de viento para tocar una nota determinada. El
conjunto de todas las posturas que se podían utilizar en un instrumento
constituye lo que se conoce como “digitación”.
¿Cuál es la diferencia entre ambos conceptos? Los dos definen el mismo hecho,
pero visto desde dos puntos de vista complementarios. Para el músico, una
postura define cómo tiene que poner las manos sobre el instrumento, tapando
y destapando agujeros, para que suene una determinada nota. Para el
ingeniero, el estado de los sensores es la forma de detectar la postura del
músico en un instante determinado de su interpretación, para luego generar
una nota u otra.
Por último, tenemos el concepto de “Nota MIDI”. La cuestión planteada es la
siguiente: Ante una postura determinada (o, equivalentemente, estado de los
“sensores”), ¿qué nota hemos de generar? En principio no existe una
equivalencia exacta, en el sentido que en un instrumento una postura produce
una nota determinada, mientras que en otro instrumento esa misma postura
produce otra nota distinta.
Éste hecho es particularmente cierto para casos en el que existen variantes de
un mismo instrumento, como puede ser como sucede con la gaita gallega.
Existen tres variantes especialmente importantes: La gaita en do (redonda), la
gaita en re (requinta) y la gaita en si bemol (tumbal). Se trata del mismo
instrumento, pero construido de tal forma que su escala principal se
corresponde con una escala de do, una escala de re en el segundo, y una
escala de si bemol, respectivamente, como cabría esperar. En esas tres gaitas,
la misma postura produce notas distintas, según la escala en la que estén
afinadas.
Por tanto, es interesante que se pueda configurar la relación entre “posturas” y
“notas MIDI” utilizando alguna herramienta gráfica. Dicha configuración
debería entonces ser almacenada en el microcontrolador, para que traduzca
las posturas indicadas a las “notas MIDI” que el usuario desea.
La implementación de la relación entre “posturas” y “notas MIDI” se realiza
mediante una tabla de correspondencia almacenada en memoria. Como el
número de posturas utilizadas en la práctica por un instrumento es muchísimo
menor que la combinación total de posibles posturas determinadas por nueve
agujeros (2^9 = 512 posturas posibles, mientras que, tirando por lo alto, se
utilizarán 30 posturas), lo más recomendable es realizar una búsqueda
secuencial hasta encontrar la nota correspondiente al estado actual.
Implementación de la Tabla de Correspondencia Posturas – Notas MIDI
Los datos que debería contener la tabla son los siguientes:
● El estado de los ocho sensores (recordemos que el noveno era de control,
por lo que no aporta información sobre la nota en cuestión). Dicho valor
será sobre el que se realicen las búsquedas en memoria.
● El identificador MIDI de la nota que vamos a generar o a apagar: 60 para
la nota Do, 61 para la nota Do#, etc.
Por tanto, cada fila de la tabla tendría una longitud de dos bytes. Un byte para
el estado de los ocho sensores, y otro byte para representar una de las 128
notas posibles dentro de los identificadores MIDI.
Ejemplo: Supongamos una gaita gallega afinada en re, que utilice digitación
abierta. Asumiendo que cada bit, una posible tabla de correspondencia
Posturas – Notas MIDI podría ser la siguiente:
Postura Nota MIDI
11111111 61 – Do#
11111110 62 – Re
11111100 64 – Mi
11111010 65 – Fa
11111000 66 – Fa#
11110000 67 – Sol
11100000 69 – La
11010000 70 – Sib
11000000 71 – Si
10100000 72 – Do
10000000 73 – Do#
00000000 74 – Re
01111110 74 – Re
01111100 76 - Mi
La digitación anterior contiene las posturas correspondientes a las notas de
mayor uso en la música tradicional gallega, por lo que constituiría un ejemplo
completamente real. Notar que existen dos posturas, la 00000000 y la
01111110, que se corresponden con la misma nota: La 74, un Re. No es raro
que en un instrumento existan varias posturas alternativas para una misma
nota. Es más, incluso existen posturas que se corresponden con notas “falsas”
(en el sentido que no tienen exactamente la misma frecuencia que su
correspondiente nota real), que se utilizan por comodidad al tocar notas
rápidas, como puede ser el caso de los trinos.
En los instrumentos reales, la correspondencia entre posturas y notas viene
dado por cómo está construido. Sin embargo, aquí la correspondencia la
establecemos nosotros, por lo que podríamos inventarnos una digitación que
fuese cómoda para tocar. Podría ser una ventaja en relación de los
instrumentos electrónicos en relación a los instrumentos tradicionales.
Supongamos un ejemplo de funcionamiento. En un instante determinado, el
usuario está tocando la postura 11110000. Si al instante siguiente cambia a la
postura 11100000, saldremos del bucle de espera, apagaremos la nota
correspondiente a la postura 11110000 (que era un Sol, según la tabla
anterior) e iniciaremos la ejecución de la nota correspondiente a la postura
11100000 (según la tabla, un La). Eso es exactamente lo que ocurriría en un
instrumento real. Siguiendo con el ejemplo, ahora nuestra postura de
referencia para realizar futuras comparaciones será la postura 11100000.
Cuando el usuario cambie nuevamente de postura, tendremos que apagar la
nota correspondiente a la postura 11100000, e iniciaremos la ejecución de la
nota correspondiente a la postura actual del usuario. Y así sucesivamente.
Una mejora en la Tabla de Correspondencias
En la tabla anterior, el valor asociado a una postura obtiene el identificador
MIDI que finalmente se transmitirá. Sin embargo, observemos la siguiente tabla
alternativa:
Postura Nota MIDI
11111111 0
11111110 1
11111100 3
11111010 4
11111000 5
11110000 6
11100000 8
11010000 9
11000000 10
10100000 11
10000000 12
00000000 13
01111110 13
01111100 15
La tabla se ha construido restando a todos los valores de la columna Nota MIDI
el valor más pequeño, el 61, correspondiente a la postura 11111111. Como
podemos observar, ahora el valor mínimo es cero, y el máximo quince. Podría
ser útil para intentar ahorrar espacio en memoria, pues la segunda columna de
la tabla podría ser almacenada ahora en cuatro bits. Sin embargo, el objetivo
es otro.
Al restar el valor más pequeño a la tabla, hemos perdido el concepto de “nota
absoluta”. Pero nos hemos quedado con otro concepto más importante, el de
“nota relativa”. La postura 11111111 produce una nota más grave que la
postura 11111110, de exactamente un semitono. ¿Por qué? Pues porque la
postura 11111111 tiene asociado un 0, y la postura 11111110 tiene asociado
un 1. Podríamos asignar a la postura 11111111 un Do, y a la postura 11111110
un Do#. O podríamos asignar a la postura 11111111 un Sol, y a la postura
11111110 un Sol#.
Podemos añadir a nuestro diseño un parámetro más, configurable. Es el valor
de la nota correspondiente a la postura 11111111. Dicho valor se sumará a
todas las correspondencias obtenidas a partir de la tabla. Para obtener el
mismo resultado que la tabla propuesta originalmente, basta con configurar
dicho parámetro a 61.
Por ejemplo, supongamos que el parámetro está configurado, efectivamente, a
61. Si nos llega la petición 11111111, obtendremos el valor cero, sumado a 61,
obtenemos 61. Si nos llega la petición 11111110, obtendremos el valor uno,
sumado a 61, resulta en 62. Y así sucesivamente.
Muchos teclados incorporan ésta habilidad, que se denomina “transportar”, y
que permite que un músico, tocando una melodía de la misma forma, pueda
obtener el sonido resultante en cualquier tonalidad. En el caso de la OpenPipe,
podríamos tocar con una gaita en do, en si bemol o en re, simplemente
configurando el parámetro anterior.
Puerto MIDI
Es muy sencillo encontrar microcontroladores que llevan incorporado el puerto
serie, como es el caso del 8051.
A continuación se indica cómo se puede obtener la salida MIDI a partir del
puerto serie:
Y, para aquellos que les interese, a continuación se indica cómo se puede
obtener la entrada MIDI a partir del puerto serie (nosotros no lo utilizaremos):
Características del Protocolo MIDI
José Soler Sáez [2], en su proyecto de fin de diplomatura “La Norma Standard
MIDI”, comenta las características principales del protocolo MIDI. A
continuación seleccionaré las partes de su memoria que son de especial
interés para nosotros:
“El interface MIDI es un interface digital que trabaja en serie y que, de alguna
forma, es bidireccional. Un interface digital transmite la información como
trenes de números binarios. En MIDI, estos trenes de números tienen ocho bits
de longitud, con lo cual podremos tener hasta 256 valores posibles. Un
interface serie trasmite estos números uno tras otro y, por lo tanto, sólo
requiere un cable con dos conductores”.
“Desde el punto de vista del hardware, el interface MIDI opera a 32,25 Kilo
baudios…” (32,25 Kilobits por segundo) “…de forma asíncrona, con un bit de
arranque (start bit), 8 bits de datos (numerados del cero al siete) y un bit de
parada (stop bit). Esto representa un total de 10 bits para un periodo de 320
microsegundos por byte serie. El bit de arranque (start bit) es un 0 lógico
(corriente desactivada) y el bit de parada es un 1 lógico (corriente activada).
Se envía en primer lugar el LSB” (el bit menos significativo).
“El circuito es del tipo lazo de corriente de 5 mA. Una salida sólo deberá
alimentar una entrada. Para evitar lazos de masa, y los consiguientes errores
en los datos, la circuitería emisora y la receptora se encuentran separadas
internamente por un optoacoplador (un diodo emisor de luz y un sensor
fotosensible, alojados en el mismo encapsulado). El receptor debe precisar
menos de 5 mA para activarse. Los tiempos de subida y bajada deben ser
menores a 2 microsegundos”.
“Los conectores son DIN de 5 pines (180 grados). Los conectores deben ser
identificados como MIDI IN y MIDI OUT. Obsérvese que los pins 1 y 3 no se
utilizan, por lo que deben dejarse sin conexión tanto en el transmisor como en
el receptor. El pin 2 del conector MIDI IN también debe dejarse sin conexión”.
“El apantallamiento de masa de los jacks MIDI no debe ser conectado a la
masa de ningún otro circuito o toma de tierra de chasis”.
(por completar)
Referencias del documento
[1]
http://www.fic.udc.es/HarvestExternalData.do?operation=directory.teacher&id=90
José Juan Lamas Seco
Departamento de Electrónica y Sistemas
Facultade de Informática
Universidade da Coruña
[2]
La Norma Standard MIDI
José Soler Saez