Simula tus propias campanadas con secuencias MIDI en Java

Simula tus propias campanadas con secuencias MIDI en Java
Facebook Twitter Flipboard E-mail

¿Nervioso por atragantarse con las uvas esta noche?. En este post, hemos preparado un entorno de test simulado para que puedas practicar antes para prevenir cualquier eventualidad o percance con las uvas como empezar con los cuartos, que te de la risa tonta, que el tamaño de las uvas no sea el adecuado, etc.

Este entorno se trata de un programa realizado en Java que mediante la creación de una secuencia MIDI pretende realizar el aviso previo, los cuartos, las doce campanadas y, como no, los aplausos finales. Como pretexto explicaré un poco como lo hemos preparado y como funciona.

Secuenciación MIDI

El formato MIDI es uno de los formatos más famosos que existen en composición musical. Estos no utilizan audio propio sino que tan solo contienen las notas y otros eventos para la ejecución de una canción o secuencia.

Al no llevar los samples incorporados en el fichero, una misma canción puede sonar bien o mal, según la tarjeta o reproductor que se utilice. No cabe duda que una gran ventaja de este formato es la baja capacidad que precisa para trasportarlo y ejecutarlo.

Contenido de un fichero MIDI

Cuando queremos crear un fichero o secuencia MIDI mediante programación debemos almacenar lo que se denomina Eventos. Un evento es cualquier cosa instantánea que puede pasar durante la canción como puede ser pulsar una tecla, soltar una tecla, cambiar de instrumento, etc. Por ello, no se trata de poner las notas con su longitud, (blanca, corchea, etc), sino cuando se pulsa una tecla y cuando se suelta.

Para los que no estén familiarizados con el MIDI, comentar que hay un número estándar de 128 instrumentos de manera que cada instrumento siempre tiene el mismo identificador. Todos los reproductores de MIDI tienen como 0 el Grand Piano o el 104 el Sitar. Para la realización de las campanadas utilizaremos el instrumento 14 Tubular Bells y finalmente el 126 Aplausos.

Por último, mencionar que los MIDIs disponen diferentes canales para la reproducción de diferentes instrumentos de manera concurrente. Existe un canal especial con número 9 para percusión. Todo esto lo veremos en futuros post cuando hagamos más experimentos con MIDI.

Secuenciador con Java

Para la creación de secuencias MIDI, Java dispone de unas clases que se encargan de cargar, modificar y reproducir secuencias MIDI. De momento, veremos por encima algunas cosas esenciales. La clase Sequence es la encargada de contener la información del MIDI. A través de sus Tracks (pistas) es capaz de almacenar eventos MIDI como pulsación o finalización de notas.

Los eventos que vamos a utilizar para la simulación de las campanadas es: PROGRAM CHANGE para la selección de un instrumento, NOTE ON para pulsar una nota y NOTE OFF para soltar la nota. Cada evento que deseamos generar se realiza mediante la clase MidiEvent.

Para concluir con esta introducción comentar que todas las canciones tienen una velocidad de ejecución en el que se denomina BPM. Este valor indica cuantas notas negras se desean ejecutar en un minuto. Cuanto más alto sea el valor, más rápida irá la canción. Al no ser una canción, para nosotros no es muy importante dado que la distancia entre notas la realizamos de otra manera. Nosotros hemos elegido un valor 120 que suele ser el habitual.

Simulador de campanadas

Finalmente, para la simulación de las campanadas hemos dividido el algoritmo en 4 partes, llamada de atención, cuartos, las doce campanadas y los aplausos. En la llamada de la atención es un golpeteo muy rápido. Para que se note el carácter humano de los golpes hemos puesto un random en la distancia para que no parezca muy mecánico o sintetizado.

Para los cuartos hemos incluido dos campanadas con diferente nota y volumen por cada cuarto. Las doce campanadas es el más fácil. Consiste en repetir 12 veces el sonido de la campanada. Finalmente, los aplausos hemos reproducido un sonido largo con el instrumento 126. Como en muchas tarjetas de sonido al reproducir una nota larga de aplauso se nota el bucle y queda muy sintético, hemos añadido varias notas para que no se note este bucle.

Código fuente

Finalmente, aquí tenéis el código fuente para ensayar las campanadas y Felíz Año 2012:


import javax.sound.midi.*;
public class Campanadas {
    public static void sonarCampana(Track pista, int nota, int pos, int vol) throws InvalidMidiDataException{
            ShortMessage on = new ShortMessage( );
            on.setMessage(ShortMessage.NOTE_ON,  0, nota, vol);
            pista.add(new MidiEvent(on, pos));
        
            ShortMessage off = new ShortMessage( );
            off.setMessage(ShortMessage.NOTE_OFF, 0, nota, vol);
            pista.add(new MidiEvent(off, pos));     
    }
    
    public static void main(String arg[]){
        try{
            Sequencer sequencer = MidiSystem.getSequencer( );
            Sequence sequence = new Sequence(Sequence.PPQ, 16);
            Track pista = sequence.createTrack();
            
            sequencer.open( );  
            sequencer.setSequence(sequence);
            sequencer.setTempoInBPM(120);
            ShortMessage ins = new ShortMessage( );
            ins.setMessage(ShortMessage.PROGRAM_CHANGE,  0, 14, 0);
            pista.add(new MidiEvent(ins, 0));
            for (int n=0;n< 64;n++){
                sonarCampana(pista, 67, n*4+(int)(Math.random()*3-1), 64);
            }
            int nDes=0;
            nDes=64*4+100;
            for (int n=0;n< 4;n++){ //Los cuartos
                    sonarCampana(pista, 71, nDes+n*128,32);
                    sonarCampana(pista, 67, nDes+n*128+40,64);
            }
        nDes+=4*128+60; //Campanadas
        for (int n=0;n< 12;n++){ //Los cuartos
                    sonarCampana(pista, 69, nDes+n*110,64);
            }
        nDes+=12*110+20;
        //Aplausos
        ins = new ShortMessage( );
            ins.setMessage(ShortMessage.PROGRAM_CHANGE,  0, 126, 0);
        pista.add(new MidiEvent(ins, nDes));           
        ShortMessage on; 
        ShortMessage off;
        for (int n=50;n< 70;n+=2){
                    on = new ShortMessage( );
                    on.setMessage(ShortMessage.NOTE_ON,  0, n, 40);
                    pista.add(new MidiEvent(on, nDes));
                    off = new ShortMessage( );
                    off.setMessage(ShortMessage.NOTE_OFF, 0, n, 40);
                    pista.add(new MidiEvent(off, nDes+400));                
        }
            sequencer.addMetaEventListener(new MetaEventListener( ) {
            public void meta(MetaMessage m) {
                    if (m.getType( ) == 47) {
                        try{Thread.sleep(2000);}catch(Exception e){}
                        System.exit(0);
                    }
                }
            });
            sequencer.start( );
      }catch(Exception e){
        e.printStackTrace();
      }
     }
}
Comentarios cerrados
Inicio