MokaByte 56 - 8bre  2001
foto dell'autore non dispnibile
Introduzione al MIDI in Java
I puntata: come ascoltare un file MIDI
di
Stefano
Leone

Monni
Con il package javax.sound.midi fornito dal Jdk 1.3 della Sun,  il linguaggio Java ci offre un valido e completo supporto per la realizzazione, modifica, salvataggio, riproduzione di composizioni MIDI comunque complesse

Il MIDI (Musical Instrument Digital Interface) è lo standard adottato 
dall’industria musicale  per il controllo di dispositivi come sintetizzatori e schede audio. I file MIDI non contengono campioni audio, ma semplicemente una sequenza di istruzioni in formato numerico da inviare direttamente ai dispositivi audio (è sufficiente possedere  una comunissima scheda audio installata sul proprio PC), che saranno così in grado di generare un certo brano musicale in base alle istruzioni ricevute. 

Facendo un semplice paragone, si potrebbe considerare una sequenza MIDI come un vero e proprio spartito musicale, e la scheda audio come “l’orchestra” che deve eseguire il brano musicale così assegnatogli.
Un simile approccio presenta notevoli vantaggi dovuti da un lato alla estrema leggerezza di tale formato (che ne fa un candidato ideale per applicazioni su INTERNET), dall’altra alla maneggevolezza, dal momento che è molto facile intervenire personalmente per modificare le caratteristiche di un brano MIDI (come la successione delle note, gli strumenti musicali utilizzati, particolari effetti e molto altro ancora).
La mia innata  passione per la programmazione, unita a quella per la composizione musicale, mi hanno indotto ad approfondire lo studio di tale package, tanto da dedicarvi una vera e propria tesina per il corso di Ing. del Software intitolata “Introduzione al MIDI in Java”, presso l’Università di Ing.Elettronica di Cagliari.
Ho pensato dunque di riorganizzare il materiale di tale tesina in una serie di articoli pratici, con l’obiettivo principale di fornire delle conoscenze di base che consentano a chiunque di avvicinarsi gradualmente all’ambito musicale-informatico del MIDI, tramite l’utilizzo del package del JDK 1.3 javax.sound.midi.
 
 
 

Per incominciare…
Per  incominciare, in questa prima puntata analizzeremo un semplice esempio che ci consentirà di indagare sui dispositivi MIDI installati correntemente sul nostro sistema e quindi di aprire ed ascoltare un file MIDI.
Qui di seguito, per comodità, si riporta subito il codice dell’intero programma, ridotto ai minimi termini per motivi di semplicità e descritto in seguito nei dettagli.

import java.io.File;
import java.io.IOException;
import javax.sound.midi.*;
 

public class Midi1 { 
  public static void main (String[] args){ 
     System.out.println("Esempio n.1 sul MIDI \n");

     // Verifichiamo la corretta installazione dei dispositivi 
     //  MIDI sul nostro sistema:

     int num_device = MidiSystem.getMidiDeviceInfo().length;
     // se non è installato almeno un 
     // dispositivo MIDI
     // l'applicazione può dirsi conclusa:
     if (num_device<1){
       System.out.println("Il sistema non possiede alcun 
                           dispositivo MIDI installato \n");
       System.exit(0);
      }
      System.out.println("Elenco dei dispositivi MIDI 
                          installati:\n");

    // Indaghiamo sui dispositivi MIDI installati e 
    // visualizziamo il loro nome
    for (int i=0;i<num_device;i++)
      System.out.println(
         MidiSystem.getMidiDeviceInfo()[i].getName());
      System.out.println("\n" + 
                         "Numero dei dispositivi installati:" +
                         String.valueOf(num_device) + "\n");
      // Procediamo ora al caricamento del file MIDI
      Sequence  mySequence;
      Sequencer mySequencer;
      File myMidiFile;

      try{
        mySequencer = MidiSystem.getSequencer(); 
        mySequencer.open(); 
        myMidiFile = new File("trippygaia1.mid"); 
        mySequence = MidiSystem.getSequence(myMidiFile); 
        mySequencer.setSequence(mySequence);
        // fa iniziare l’esecuzione musicale
        mySequencer.start(); 
         System.out.println("Riproduzione della sequenza 
                           musicale del ");
        System.out.println("file:" + myMidiFile.getName()+ 
                           " in corso... \n");
        System.out.println("Premi INVIO per terminare la 
                            riproduzione del brano ");
        System.in.read();

        // fa terminare l’esecuzione musicale
        mySequencer.stop(); 

        System.out.print("Esecuzione terminata al secondo: ");
        System.out.println(""+
                       Sequencer.getMicrosecondPosition()/1000000);
        System.out.println();
        System.out.println("La lunghezza della sequenza MIDI 
                            era di ");
        System.out.print(""+
                       Sequencer.getMicrosecondLength()/1000000));
        System.out.println(" secondi. ");
        // chiude il dispositivo precedentemente aperto
        mySequencer.close(); 
        mySequencer = null;
        System.exit(0);
  }
  catch (MidiUnavailableException ex) {
        System.out.println("Sequencer non disponibile \n");
        System.exit(0);}

  catch (IOException ex) {
        System.out.println("Impossibile aprire il file 
                            specificato \n");
        System.exit(0);}
  catch (InvalidMidiDataException ex){ 
        System.out.println("Errore nel formato dei dati MIDI \n");
        System.exit(0);}
  } 
}
 

Commento della applicazione Midi1
La prima parte di codice, come già anticipato, ha il fine di informare l’utente sulle risorse MIDI correntemente installate nel proprio sistema.
Per fare ciò, Java ci mette a disposizione la classe MidiSystem, come specificato dalla documentazione fornita dalla Sun (si veda [2] per ulteriori informazioni): 

“The MidiSystem class provides access to the installed MIDI system resources, including devices such as synthesizers, sequencers, and MIDI input and output ports. 
A typical simple MIDI application might begin by invoking one or more MidiSystem methods to learn what devices are installed and to obtain the ones needed in that application. 
The class also has methods for reading files, streams, and URLs that contain standard MIDI file data or soundbanks. You can query the MidiSystem for the format of a specified MIDI file. 
You cannot instantiate a MidiSystem; all the methods are static.”

Relativamente al nostro caso, per il momento sarà  nostro interesse accedere a delle informazioni sui dispositivi MIDI installati , più che ai dispositivi stessi.
Per fare ciò occorrerà utilizzare il metodo 

MidiDevice.Info [] MidiSystem.getMidiDeviceInfo()

dove appunto MidiDevice.Info [] è un array  di oggetti  MidiDevice.Info, uno per ciascun dispositivo installato, che contiene informazioni di carattere generale sul dispositivo MIDI in questione (come il nome, il venditore, etc.).

Pertanto, tramite l’assegnamento

int num_device = MidiSystem.getMidiDeviceInfo().length;

la variabile num_device conterrà il numero di dispositivi MIDI installati, mentre
il metodo

MidiSystem.getMidiDeviceInfo()[i].getName()

restituirà una stringa contenente il nome dell’ i-esimo dispositivo installato.
L’applicazione avrà subito termine nel caso non venga rilevato alcun dispositivo MIDI (ossia nel caso risulti num_device = 0).
In caso contrario, sarà possibile procedere nel tentativo di apertura del nostro file MIDI.
Come prima cosa, vengono dichiarate tutte le variabili  necessarie per il caricamento e l’ascolto del file MIDI:

File      myMidiFile;
Sequence  mySequence;
Sequencer mySequencer;

dove 

File myMidiFile 

dovrà contenere il file MIDI da ascoltare e dove 

Sequence mySequence 

costituirà la nostra sequenza MIDI, il cui contenuto – relativamente al nostro esempio – verrà a sua volta determinato dal contenuto del file myMidiFile (tale punto verrà chiarito meglio fra breve).
Per ora ci basterà sapere che mySequence (istanza della classe Sequence) dovrà contenere tutte le informazioni musicali (informazioni sul tempo ed una o più tracce MIDI) necessarie per la corretta esecuzione del brano.
Per essere eseguita musicalmente, una sequenza musicale (nel nostro caso mySequence), dovrà essere indirizzata ad un Sequencer (dispositivo software o hardware che consente l’esecuzione di un brano MIDI), da noi rappresentato dalla variabile  mySequencer.
In Java, col termine Sequencer si fa riferimento ad una interfaccia che include dei metodi che consentono, tra le altre cose, di (cfr.Interface Sequencer, Java API Reference, relativamente alla voce [2]): 
 

  • obtaining a sequence from MIDI file data 
  • starting and stopping playback 
  • moving to an arbitrary position in the sequence 
  • changing the tempo (speed) of playback 
  • synchronizing playback to an internal clock or to received MIDI messages 
  • controlling the timing of another device 
In addition, the following operations are supported, either directly, or indirectly through objects that the Sequencer has access to: 
 
  • editing the data by adding or deleting individual MIDI events or entire tracks 
  • muting or soloing individual tracks in the sequence 
  • notifying listener objects about any meta-events or control-change events encountered while playing back the sequence. 
Nel nostro esempio, ci accontenteremo nell’eseguire le prime due operazioni, ovvero il caricamento di una precisa sequenza sul nostro Sequencer (nel nostro caso mySequencer) e l’esecuzione musicale della sequenza stessa.
Per fare ciò, iniziamo a caricare la nostra sequenza MIDI scrivendo:

myMidiFile = new File("trippygaia1.mid"); 

che associa alla variabile myMidiFile il file chiamato “trippygaia1.mid”, e
quindi:

mySequence = MidiSystem.getSequence(myMidiFile);

che crea una nuova sequenza MIDI ottenuta dal file specificato mediante l’impiego del metodo getSequence(File MidiFile) della già menzionata classe MidiSystem.
A questo punto è necessario inizializzare il nostro sequencer, attraverso l’istruzione

mySequencer = MidiSystem.getSequencer();

che associa alla variabile mySequencer il Sequencer di default del sistema specifico in uso.
Per poter caricare la sequenza creata nel nuovo Sequencer, è prima necessario aprire quest’ultimo, tramite il metodo open(), come segue:

mySequencer.open();

A questo punto non ci rimane che caricare la nostra sequenza scrivendo:

mySequencer.setSequence(mySequence);

 e mandarla in esecuzione col metodo start(), come segue:

mySequencer.start();

Una volta che la sequenza inizia ad essere riprodotta musicalmente  dovrà essere possibile, ad un certo punto, interromperla.
Ciò è possibile mediante il metodo stop(), come segue:

mySequencer.stop();

Il metodo

long mySequencer.getMicrosecondPosition()

restituisce la posizione corrente nella sequenza MIDI (espressa in microsecondi)
mentre il metodo

long mySequencer.getMicrosecondLength()

restituisce, sempre in microsecondi, la durata totale della sequenza MIDI corrente.
E’ molto importante, alla fine della applicazione, chiudere tutti i dispositivi MIDI aperti in precedenza, per evitare eventuali problemi che possono sorgere, altrimenti, al riavvio della applicazione stessa o all’avvio di altre applicazioni.
Nel nostro caso, sarà sufficiente chiudere il nostro Sequencer e farlo puntare a null, come segue:

mySequencer.close();
mySequencer=null;
 

Per concludere, diamo un rapido sguardo alla gestione delle eccezioni.
Nel blocco

catch (MidiUnavailableException ex) {
  System.out.println("Sequencer non disponibile \n");
  System.exit(0);
}

vengono raccolte le eventuali eccezioni gestite dalla classe MidiUnavailableException, che vengono lanciate quando un componente MIDI (nel nostro caso mySequencer) non può essere aperto o creato poiché non è disponibile.
Nel blocco

catch (InvalidMidiDataException ex){ 
 System.out.println("Errore nel formato dei dati MIDI\n");
 System.exit(0);
}

vengono invece raccolte le eventuali eccezioni gestite dalla classe InvalidMidiDataException, che vengono lanciate quando si incontrano dei dati in un formato diverso da quello MIDI, e quindi non interpretabili dal dispositivo che tenta di leggerli. 
Relativamente al nostro esempio, tale eccezione potrebbe essere sollevata in seguito all’istruzione

mySequence = MidiSystem.getSequence(myMidiFile);

qualora myMidiFile non rappresenti un file in un formato MIDI valido, oppure in seguito all’istruzione

mySequencer.setSequence(mySequence);

qualora la variabile mySequence dovesse contenere una sequenza di dati MIDI in un formato non corretto (o comunque non supportato dal Sequencer).
 
 
 

Osservazioni finali
Nel caso di caricamento di un file MIDI su una Sequence, possiamo quindi notare come per poter ascoltare il brano musicale non sia necessaria nessuna operazione aggiuntiva se non quella di caricare la sequenza stessa sul Sequencer e mandare quest’ultimo in esecuzione.
Questo capita poiché la sequenza conterrà già in sé tutte le informazioni necessarie (sul tempo, sulle note, sugli strumenti da usare, ecc.) per la corretta esecuzione di un brano musicale, essendole state direttamente fornite dal file MIDI stesso.
Nelle prossime puntate verrà spiegato come costruire da sé una sequenza MIDI “partendo da zero” (senza cioè dover necessariamente ricorrere ad un file MIDI per generarla) e come poi salvarla sotto forma di file MIDI.
Quando invece ci si vuole limitare a generare dei suoni, è possibile evitare sia il ricorso alla Sequence che al Sequencer tramite l’uso di un sintetizzatore Midi, supportato in Java dall’interfaccia Synthesizer.
Nella prossima puntata verrà fornito un esempio pratico su come generare i suoni col sintetizzatore MIDI tramite  l’uso dell’interfaccia Synthesizer, insieme alla descrizione delle classi ed interfacce ad esso correlate. 
 
 
 

Bibliografia

[1] S.Monni – “Introduzione al MIDI in Java”,  Tesina di Ing. del Software presso l’Università di Ing.Elettronica di  Cagliari –  gennaio 2001

[2] Documentazione fornita dalla Sun su Midi consultabile all’indirizzo:
http:// java.sun.com/j2se/1.3.0/docs/api/javax/sound/midi/package-summary.html
 

Vai alla Home Page di MokaByte
Vai alla prima pagina di questo mese


MokaByte® è un marchio registrato da MokaByte s.r.l.
Java®, Jini®  e tutti i nomi derivati sono marchi registrati da Sun Microsystems; tutti i diritti riservati
E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a info@mokabyte.it