domingo, 29 de abril de 2012

Síntesis FM. Polifonía

Ahora que ya tenemos la capacidad de producir sonidos simples, lo primero que nos llama es la necesidad de proporcionar polifonía a nuestro sintetizador para poder tocar varias notas simultáneamente en nuestro controlador MIDI.


Comenzamos añadiendo el objeto ‘poly’ a nuestro sintetizador monofónico, consiguiendo reproducir señales sinusoidales de distinta frecuencia (en función de las teclas presionadas) a la vez. Sin embargo, seguía siendo un sonido musicalmente pobre, así que buscamos ayuda en la red y encontramos un trozo de código llamado ‘polywavesynth’, de libre distribución. 


Gracias a este módulo de síntesis FM conseguimos sonidos más interesantes para nuestro sintetizador modulando distintos tipos de señales. Posee un generador de envolvente, un filtro paso bajo con control de frecuencia de corte y resonancia, control de balance estéreo y posibilidad de implementar fácilmente un módulo para ruedas de ‘pitch bending’ (presentes en la gran mayoría de teclados controladores para modular la frecuencia del sonido).


'as_synthModule': módulo de síntesis FM
Además, hemos añadido soporte para pedales de sustain, imprescindibles para tocar pianos y muy útiles para otros tipos de instrumentos; hemos reconfigurado el módulo de Active Sensing para que funcione con nuestro nuevo sintetizador; y por último y muy importante, hemos implementado la opción de separar y routear los flujos de datos de los 16 canales MIDI virtuales para independizarlos y otorgar un sonido distinto a cada uno de los instrumentos asignados a cada canal.


La versión final del módulo incluye la opción de añadir reverb al sonido y contiene una lista de presets, así como un submódulo que permite guardar y cargar configuraciones de parámetros en el sintetizador. Además, el audio generado se envía por dos canales a un mixer en lugar de ser dirigido directamente al DAC. De esta forma, podemos recoger todos los canales de todos los módulos en un sólo lugar cuya suma será la que enviemos finalmente a la salida de audio.


De esta forma, y como hemos podido comprobar en las pruebas, creando varios módulos sintetizadores tenemos capacidad para reproducir canciones con diferentes sonidos de diferentes instrumentos que tocan al mismo tiempo.

sábado, 28 de abril de 2012

Librería gráfica: MatrixController

Durante todo el desarrollo, hemos ido creando funciones para abstraer el funcionamiento de los canales PWM de la matriz en pos de programar basado en pixeles y colores RGB. Hace poco, hemos cogido todos estas funciones y las hemos reunido en una librería de Arduino que es la que maneja directamente nuestra librería Tlc5940Mux modificada.

La librería se puede descargar aquí: MatrixController 1.0.

Esta librería se usas creando una instancia con  
MatrixController matrix;

Se inicializa con:
matrix.init(0);

Y dispone de una serie de métodos, bastante autodescriptivos, aunque están documentados en la propia librería:

matrix.setPixel(1,2,4095,4095,0);
Pone el tercel píxel de la segunda columna (1,2) a una mezcla de rojo y verde.

matrix.setRow(0,0,0,4095);
Pone la fila superior a azul.

Los rangos de fila y columna son de 0 a 8, y los de cada canal RGB de color de 0 a 4095 (12 bits por canal, color de 36 bits).

La lista de funciones completas es:
  • init
  • setPixel
  • setRow
  • setCol
  • setMatrix
  • getPixelR
  • getPixelG
  • getPixelB
  • shiftFila
  • shiftRowData
Requiere de escribir el código de la ISR en el propio sketch que la use, aunque este es el siguiente:
/** It's 0 if there is no multiplex operation taking place currently, and different if yes.
    If everything's fine, it shouldn't be neccesary...
    */

volatile uint8_t isShifting;
/** Stores the actual row that is being excitated in the RGB display.
    */
uint8_t shiftRow;
/** Excitates the desired row. The power stage is active low.
    \param row, The index of the row that should be activated. */
inline void setRow(int row) {
  PORTC = row;
}
/** Interrupt subrutine for Timer1, called after each PWM cycle.
    It switchs the row and shifts the new colour data.
    */
ISR(TIMER1_OVF_vect)
{
  if (!isShifting++) {
   
    digitalWrite(BLANK, HIGH);  // turn off the outputs. It's the first thing to avoid row interference
    __asm__("nop\n\t");  // small delay after sending the data before transfering it to the registers
    // set the registers
    digitalWrite(XLAT, HIGH);             
    __asm__("nop\n\t");      
    digitalWrite(XLAT, LOW);     
    __asm__("nop\n\t");  // delay before turning on the outputs
    setRow(shiftRow);          // set the new row
    digitalWrite(BLANK, LOW);      // turn on the outputs 
    if (++shiftRow == NUM_ROWS){  // update the row variable
      shiftRow = 0;
    }
    matrix.shiftRowData(shiftRow);  // shifts the data of the next row, it only will take place in the following row change
    isShifting = 0;
  }
}


Este código se encarga de realizar los cambios de fila.

Para comprender el funcionamiento y uso, ver el sketch de ejemplo incluido en la carpeta examples de la librería de Arduino.

martes, 24 de abril de 2012

Presentación Hito 2

El pasado viernes 20, realizamos la presentación del hito 2 ante el DIE y los autores de las demás prácticas innovadoras del curso.


La presentación mostrada se puede descargar aquí.


En ella hemos presentado una aproximación de visualización audiorítmica, así como los progresos comentados aquí y algunas pinceladas que están pendientes de publicarse. También hemos comentado el trabajo destinado al hito final.

La visualización mostrada es la siguiente:

En el video NO se corresponde con la música, en la presentación sí que se mostró comanda por una fuente MIDI.

Interfaces serie: librerías e interrupciones

AtMega 2560 (4xUSART).
El entorno Arduino implementa la comunicación serie a través de la libreria integrada Serial. Esta se encarga de manejar las interupciones de las USART/UART de Arduino, configurar el hardware y gestionar el flujo de datos.
Particularmente, maneja las interrupciones de dato recibido y de dato transmitido, añadiendo o recogiendo los datos de sendos bufferes circulares, de forma que un sketch de Arduino pueda mandar datos a la UART sin preocuparse de si la unidad tiene trabajo en ese momento o no.
Esto trae el inconveniente de la recepción de datos, se ha de consultar periódicamente en el programa si se han recibido datos nuevos.
La referencia de la librería se encuentra aqui.


Con nuestro sistema, hemos experimentado pérdida de tramas, es decir, algunos bytes que deberían ser redirigidos desde el puerto MIDI al PC se pierden. Esto ocurre particularmente a velocidades altas de notas usando varios canales o instrumentos a la vez.
Al basarse la librería Serial en bufferes, nos lleva a pensar que se pierden las tramas al llenarse el buffer de recepción antes de que nuestro programa los procese.

Para solucionarlo, hemos estado leyendo acerca de manejar la UART directamente, sin la librería de Arduino, y procesar nosotros mismos las interrupciones. En concreto, la mayoría de información la hemos obtenido de este post del foro de Arduino: Interrupt-driven USART reception: I'm stumped.

Usando parte del código allí posteado, y cambiando algunos datos, como los vectores de interrupcion USART0_RX_vect y USART1_RX_vect, hemos hecho la librería de comunicaciones para nuestro sistema.

Se puede descargar aqui.

Contiene tanto los archivos ArdSynthComm.h, ArdSynthComm.cpp y keywords.txt (resaltado de sintaxis del entorno Arduino), como un par de ejemplos de uso: USBecho y MIDIreplicator. En sendos ejemplos se describe su función.


jueves, 12 de abril de 2012

It's all about timing: correcciones en la matriz RGB

Matriz.
Con la matriz, ya habíamos comentado el problema de interferencia entre filas aqui.

Tras examinar exhaustivamente los cronogramas producidos por el micro, el problema consistía en proceso de cambio de fila. Este, al principio, seguía el siguiente esquema:
  • Cambio de fila
  • Envío de datos de la nueva fila
  • Activado de señal BLANK para resetear salidas y de señal XLAT para escribir los nuevos datos.
  • Desactivado de XLAT y BLANK.
El problema residía en que, durante el tiempo que tarda en enviarse los datos de la fila i+1, los colores de la fila i se estaban pintando en la fila i+1 ya que se había cambiado la fila al principio.

La primera opción es modificar la rutina de interrupción para que sigua el siguiente esquema:
  • Activado señal BLANK
  • Envío de datos de la nueva fila
  • Activado de señal XLAT
  • Cambio de fila
  • Desactivado de señal XLAT y BLANK
Esto elimina los problemas de filas, pero nos ha llevado a ver un problema de temporización.
Cronograma, con errores de temporiz.
Para explicarlo claramente, es mejor examinar el cronograma de la imagen (pinchar para ampliar).
  • En él, tenemos la señal GSCLK, generada por el Timer2, de forma continua, a 8 Mhz y que se encarga de incrementar los contadores.
  • El timer 1 interrumpe cada 4096 ciclos de GSCLK y se encarga de activar las señales BLANK, XLAT y transmitir por SPI, además de cambiar de fila.
  • Las salidas del TLC5940 funcionan cuando la señal BLANK está a nivel bajo, y mientras no se hayan recibido más de 4096 ciclos de GSCLK tras la última bajada de BLANK.
El problema viene dado por el tiempo que BLANK está a nivel alto, ya que, si se cambia de fila cada 4096 ciclos de GSCLK, y BLANK está a nivel alto cierto tiempo, resulta en ciclos de GSCLK que no se realizan ya que no da tiempo entre los pulsos de BLANK, reduciendo la gama cromática, y no representando los colores adecuadamente.

La solución más básica es aumentar el número de ciclos de GSCLK que pasan entre cada interrupción para el cambio de fila, de forma que quepan 4096 ciclos (512 microsegundos) entre cada pulso de BLANK. Para realizarlo de forma aproximada, hemos medido dicho tiempo con el osciloscopio, y ajustado el valor del registro de captura del Timer (ICR1) para que coincida.
Esto, evidentemente, conlleva una perdida en la velocidad de refresco, quedandose en 200 Hz (en vez de los 244 Hz originales), ya que se necesitan 1120 ciclos más de GSCLK para que todo cuadre.


Tras esta corrección, la parte de control de la matriz podría haber quedado así, pero encontramos una solución mejor para optimizar el refresco.
El chip TLC5940 admite recibir datos durante un ciclo de PWM, y solo escribe los valores cuando llega la señal XLAT, luego, podemos transmitir los valores de la nueva fila mientras se está iluminando la anterior.
Resulta el siguiente esquema:
  • Activar señal BLANK 
  • Activar señal XLAT
  • Cambio de fila
  • Desactivado de BLANK y XLAT, la matriz empieza a iluminarse
  • Envio de los datos de la siguiente fila.
Con esta técnica, la duración del pulos de BLANK es mucho menor. Ajustando la temporización de la forma comentada anteriormente, sólo necesitamos 170 ciclos extra de GSCLK, y el refresco se queda en 235 Hz.

Con esto, creemos finalizado el diseño de la parte de la matriz RGB, aunque posteriormente se podría implementar la lectura de la flag de error XERR, dot correction o lectura del registro de estado, pero no son imprescindibles para el proyecto.
Cuando acabemos la temporización del micro, intentaremos comproblar con un analizador lógico que toda la temporización funciona de forma adecuada.