#!/usr/bin/python -u
# coding: utf-8

#--------------------------------------------------------------------
# Fichero:  ca0008.py
# Objetivo: Composición algorítmica 0008
# Autor:    Pedro Reina
# Fecha:    D.17.7.2016
# Licencia: CC0 1.0 Universal
#           https://creativecommons.org/publicdomain/zero/1.0/
# Historia:
#   J.18.6.2015: versión inicial
#   D.17.7.2016: pythonización
#--------------------------------------------------------------------

#------------------------------------------------
# Constantes de la composición
#------------------------------------------------

# Cuántos pulsos tendrá la composición
Pulsos = 250

# El tiempo de espera inicial entre cada pulso
Espera = 0.23

# Si se desea ver mensajes de depuracion
Depuracion = 0

#------------------------------------------------
# La función que realiza la composición
#------------------------------------------------

def Composicion():
    global Espera

    Piel1 = MIDI_InstrumentoPiel()
    Piel2 = MIDI_InstrumentoPiel()
    Metal = MIDI_InstrumentoMetal()

    Contador1 = 0
    Contador2 = 1
    Contador3 = 2

    # Generamos los pulsos
    for i in range(1, Pulsos):

        # Mostramos el contador de pulsos
        CA_Texto('('+str(i)+')')

        MIDI_Nota(Piel1, MIDI_ListaVolumen[Contador1])
        MIDI_Nota(Piel2, MIDI_ListaVolumen[Contador2])
        MIDI_Nota(Metal, MIDI_ListaVolumen[Contador3])

        Contador1 += 1
        if Contador1 == 4:
            Contador1 = 0
            Piel1 = MIDI_InstrumentoPiel()

        Contador2 += 1
        if Contador2 == 4:
            Contador2 = 0
            Piel2 = MIDI_InstrumentoPiel()

        Contador3 += 1
        if Contador3 == 4:
            Contador3 = 0
            Metal = MIDI_InstrumentoMetal()

        # Esperamos un momento
        time.sleep (Espera)

        # A veces modificamos el tiempo de espera
        if i%25 == 0:
            Espera -= 0.015

#------------------------------------------------
# Módulos necesarios
#------------------------------------------------

# Para gestionar el entorno MIDI
import pypm

# Para manejar el reloj
import time

# Para generar números aleatorios
import random

#------------------------------------------------
# Constantes del sistema
#------------------------------------------------

# El nombre de la unidad MIDI out
MIDI_SALIDA = 'USB Midi MIDI 1'

# El código MIDI para lanzar un evento Note On en el canal 10
MIDI_NOTAON = 0x99

# El código MIDI para lanzar un evento Note Off en el canal 10
MIDI_NOTAOFF = 0x89

# El valor mínimo del volumen
MIDI_SILENCIO = 0x00

# Las listas de instrumentos válidos
MIDI_ListaPiel = (35, 36, 37, 38, 39, 40, 41, 43, 45, 47, 48, 50, 58, 62, 63,
                  64, 75, 82, 84, 85, 86, 87, 88, 89, 91, 93)
MIDI_ListaMetal = (42, 44, 46, 49, 51, 53, 54, 55, 56, 57, 67, 68, 69, 90)

# Usaremos solo estos cuatro volúmenes
MIDI_ListaVolumen = (8, 32, 64, 127)

#------------------------------------------------
# Variables globales del sistema
#------------------------------------------------

# El manejador para enviar eventos MIDI
MIDI_Out = 0x00

#------------------------------------------------
# Funciones
#------------------------------------------------

#------------------------------------------------
# Escribe un texto
def CA_Texto (Mensaje):
    global Depuracion

    # Si nos lo piden, imprimimos el texto
    if Depuracion:
        print Mensaje,

#------------------------------------------------
# Inicia todo el sistema MIDI
def MIDI_Inicio():
    global MIDI_TotalPiel, MIDI_TotalMetal, MIDI_Out

    # Iniciamos el sistema Python-MIDI
    pypm.Initialize()

    # Obtenemos la unidad que nos interesa
    Unidad = MIDI_BuscaSalida()

    # La imprimimos
    CA_Texto(MIDI_SALIDA + ' => ' + str(Unidad) + '\n')

    # Abrimos el puerto MIDI
    Latencia = 0
    MIDI_Out = pypm.Output(Unidad, Latencia)

    # Vemos cuántos instrumentos tenemos
    MIDI_TotalPiel = len(MIDI_ListaPiel)
    MIDI_TotalMetal = len(MIDI_ListaMetal)

    # Informamos
    CA_Texto('Instrumentos de piel => ' + str(MIDI_TotalPiel) + '\n')
    CA_Texto('Instrumentos de metal => ' + str(MIDI_TotalMetal) + '\n')

#------------------------------------------------
# Termina todo el sistema MIDI
def MIDI_Final():
    global MIDI_Out

    # Eliminamos la salida MIDI
    del MIDI_Out

    # Terminamos el sistema Python-MIDI
    pypm.Terminate()

#------------------------------------------------
# Envía una nota a la unidad MIDI
def MIDI_Nota (Instrumento, Volumen):
    # Si nos lo piden, imprimimos el instrumento
    if Depuracion:
        print Instrumento,

    # Nota on
    MIDI_Out.WriteShort(MIDI_NOTAON, Instrumento, Volumen)

    # Nota off
    MIDI_Out.WriteShort(MIDI_NOTAOFF, Instrumento, MIDI_SILENCIO)

#------------------------------------------------
# Elige una piel al azar
def MIDI_InstrumentoPiel():
    return random.choice(MIDI_ListaPiel)

#------------------------------------------------
# Elige un metal al azar
def MIDI_InstrumentoMetal():
    return random.choice(MIDI_ListaMetal)

#------------------------------------------------
# Busca el número de la salida MIDI
def MIDI_BuscaSalida():

    # Vemos cuántas unidades MIDI hay disponibles
    TotalUnidades = pypm.CountDevices()

    # Las examinamos todas
    for Numero in range(TotalUnidades):

        # Obtenemos los datos de la unidad
        (Interfaz, Nombre, Entrada, Salida, Estado) = pypm.GetDeviceInfo(Numero)

        # Si es una unidad de salida
        if Salida == 1:

            # Vemos si es la que queremos
            if Nombre == MIDI_SALIDA:
                Respuesta = Numero

    # Devolvemos el resultado
    return Respuesta

#------------------------------------------------
# El programa
#------------------------------------------------

# Iniciamos el sistema
MIDI_Inicio()

# Hacemos la composición
Composicion()

# Terminamos el sistema
MIDI_Final()
