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

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

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

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

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

# El tiempo de espera entre cada pulso
Espera = 0.1

# Si se desea ver mensajes de depuracion
Depuracion = 0

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

def Composicion():

    # Calculamos el total de pulsos que vamos a generar
    Duracion = Compases * Pulsos + 1

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

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

        Modulo = i%8

        if i%16 == 1:
            Instrumento1 = MIDI_Instrumento()
            Instrumento2 = MIDI_Instrumento()
            Instrumento3 = MIDI_Instrumento()
            Instrumento4 = MIDI_Instrumento()

        if Modulo in (0,2,4):
            MIDI_Nota(Instrumento1, MIDI_VOLUMEN/3)

        if Modulo in (1,3,5):
            MIDI_Nota(Instrumento2, MIDI_VOLUMEN/3)

        if Modulo in (0,1,4,5):
            MIDI_Nota(Instrumento3, MIDI_VOLUMEN)

        if Modulo in (4,6):
            MIDI_Nota(Instrumento4, MIDI_VOLUMEN)

        # Esperamos un momento
        time.sleep (Espera)

#------------------------------------------------
# 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áximo del volumen
MIDI_VOLUMEN = 0x7F

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

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

#------------------------------------------------
# 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_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 tenemos
    CA_Texto('Instrumentos => ' + str(len(MIDI_ListaInstrumento)) + '\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 un instrumento al azar
def MIDI_Instrumento():
    global MIDI_TotalInstrumento
    return random.choice(MIDI_ListaInstrumento)

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