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

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

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

# Cuántos compases tendrá la composición
Compases = 30

# Cuántos pulsos tendrá cada compás
Pulsos = 32

# El tiempo de espera entre cada pulso
Espera = 0.07

# Si se desea ver mensajes de depuracion
Depuracion = 0

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

def Composicion():

    TopePiel1 = 5
    TopePiel2 = 90

    TopeMetal1 = 10
    TopeMetal2 = 70

    Piel1 = MIDI_InstrumentoPiel()
    Piel2 = MIDI_InstrumentoPiel()

    # Generamos los compases
    for i in range(1, Compases):

        if CA_Aleatorio() < 20:
            Piel1 = MIDI_InstrumentoPiel()
        if CA_Aleatorio() < 50:
            Piel2 = MIDI_InstrumentoPiel()
        Metal = MIDI_InstrumentoMetal()
        Viento = MIDI_InstrumentoViento()

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

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

            if j in (1,2,3,7,8,9,13):
                Tope = TopePiel2
                Volumen = MIDI_ListaVolumen[3]
            else: 
                Tope = TopePiel1
                Volumen = MIDI_ListaVolumen[1]
            if CA_Aleatorio() < Tope:
                MIDI_Nota(Piel1, Volumen)

            if j in (4,5,10,11):
                Tope = TopePiel2
                Volumen = MIDI_ListaVolumen[3]
            else: 
                Tope = TopePiel1
                Volumen = MIDI_ListaVolumen[0]
            if CA_Aleatorio() < Tope:
                MIDI_Nota(Piel2, Volumen)

            if j%8 == 0: Tope = TopeMetal2
            else: Tope = TopeMetal1
            if CA_Aleatorio() < Tope:
                MIDI_Nota(Metal, MIDI_Volumen())

            if j%16 == 2:
                if CA_Aleatorio() < 10:
                    MIDI_Nota(Viento, MIDI_Volumen())

            # Esperamos un momento
            time.sleep (Espera)

        TopePiel1 += 1
        TopePiel2 += 1
        TopeMetal1 += 1
        TopeMetal2 += 1

#------------------------------------------------
# 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)
MIDI_ListaViento = (71, 72)

# 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,

#------------------------------------------------
# Obtiene un número aleatorio entre 1 y 100
def CA_Aleatorio():
    Respuesta = random.randrange(1,100)
    CA_Texto('{'+str(Respuesta)+'}')
    return Respuesta

#------------------------------------------------
# Inicia todo el sistema MIDI
def MIDI_Inicio():
    global MIDI_TotalPiel, MIDI_TotalMetal, MIDI_TotalViento
    global MIDI_TotalVolumen, 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)

    # Informamos de cuántos instrumentos y volúmenes tenemos
    # Vemos cuántos volúmenes tenemos
    CA_Texto ('Instrumentos de piel => ' + str(len(MIDI_ListaPiel)) + '\n')
    CA_Texto ('Instrumentos de metal => ' + str(len(MIDI_ListaMetal)) + '\n')
    CA_Texto ('Instrumentos de viento => ' + str( len(MIDI_ListaViento)) + '\n')
    CA_Texto ('Volúmenes => ' + str(len(MIDI_ListaVolumen)) + '\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)

#------------------------------------------------
# Elige un viento al azar
def MIDI_InstrumentoViento():
    return random.choice(MIDI_ListaViento)

#------------------------------------------------
# Elige un volumen al azar
def MIDI_Volumen():
    return random.choice(MIDI_ListaVolumen)

#------------------------------------------------
# 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()
