miércoles, 17 de septiembre de 2014

controlando un robot con señales MIDI

El estándar MIDI (Musical Instrument Digital Interface) es un protocolo muy usado en la industria musical para transmitir información entre dispositivos musicales, como teclados, samplers, equipos para sincronizar dispositivos, etc etc. Si bien es un estándar bastante antiguo, sigue siendo valido y aun hoy es una de las formas mas común de controlar distintos equipos musicales.
En los sistemas GNU/LINUX, también implementan una forma de controlar distintos software y hardware a través de señales MIDI, de esa forma, uno puede tener un controlador MIDI o un teclado con salida MIDI  y controlar distintos programas de audio como LMMS, Hydrogen, Ardour o algunos sintes digitales como qsynth.
La forma de trabajar con auido en GNU/LINUX, puede ser un tanto complicadod e entender al principio, hay varios motores de sonido (OSS, ALSA) pero ademas, esta JACK, que se encarga de capturar el hardware y ser un buffer (o proxy si se quiere) que toma las entradas y salidas de los distintos software y los envía a la placa de audio, permitiendo hacer cosas como tomar la salida de LMMS y meterla dentro de un canal de Ardour y la salida maestra de Ardour derivarla a un multi efecto como Rakarrack, y después, todo eso a la salida de la paca de audio.


Ejemplo de Jack con su front-end qjackctl y LMMS

La idea del proyecto siguiente es poder hacer una interface que tome datos MIDI a traves de Jack y las re envié al puerto /dev/ttyACM0 para poder controlar un robot y hacer música. Para eso, use las librerías de pygame para control MIDI, que si bien son unas librerias gigantes, estan empaquetadas en casi todas las distribuciones GNU/LINUX (Y obviamente en FEDORA) y hay mucha documentación. Para el control del robot usao el modulo apicaro, que forma parte del proyecto ICARO y que me permite andar señales a la placa mediante python-serial y un firmware especial del lado del PIC (firmware tortucaro, que se carga desde la pantalla de icaro-bloques).

boton de carga para el firmware Tortucaro 
(para controlar la placa desde puerto /dev/ttyACMx)




Video de un robot que toca el xilofon, 
controlado mediante LMMS y tambien un teclado controlador MIDI.

 Aca el código fuente del robot, no es muy difícil de entender, básicamente son 2 clases que controlan el MIDI y el puerto serie (usando apicaro)
import pygame
import time
import pygame.midi
import apicaro


class ROBOT:
    """ clase robot, cotrola mediante apicaro los dos servomotores
    de moviento del robot (izquierda/derecha, arriba/abajo """
    escala={
            "c":30,
            "d":60,
            "e":80,
            "f":110,
            "g":130,
            "a":155,
            "b":180,
            "c2":205
            }
    golpe={
            "sube":60,
            "baja":29
            }
    
    def __init__ (self):
        """ Class initialiser """
        
        self.icaro=apicaro.puerto()
        self.band=self.icaro.iniciar()
    def mover(self,tecla):
        self.icaro.activar_servo(1,self.escala[tecla])
    def golpear(self):
        self.icaro.activar_servo(2,self.golpe["baja"])
        time.sleep(0.07)
        self.icaro.activar_servo(2,self.golpe["sube"])
        

class MIDI:
    """ inicia pygame.midi y tiene un diccionario con algunas notas MIDI """
    notas={
            60:"c",
            62:"d",
            64:"e",
            65:"f",
            67:"g",
            69:"a",
            71:"b",
            72:"c2"
            }
    def __init__ (self):
        """ Class initialiser """
        pygame.midi.init()
        print "set player"
        self.player= pygame.midi.Input(1)
        print "set instrument"

midi=MIDI()
robot=ROBOT()
while robot.band:
    # nos fijamos si hay datos en el buffer
    flag = midi.player.poll()
    if flag == True:
        data = midi.player.read(10)
        senal = data [0][0]
        nota = senal[1]
        volumen = senal[2]
        #~ print volumen
        # si la nota esta dentro del diccionario, muevo el robot
        if midi.notas.has_key(nota):
            if volumen <>0:
                robot.mover(midi.notas[nota])
                time.sleep(0.2)
                robot.golpear()