MokaByte Numero 28  -  Marzo 1999
 
Java Sound
di 
Mauro Molino
Java entra nel mondo del sequencing audio/MIDI con una nuova API,la Java Sound


 

La nuove Java Sound API promette la possibilita' di interagire in maniera estremamente efficace con le piu' disparate sorgenti audio, operando a basso livello sul suono digitale.Nell'articolo cercheremo di entrare nel dettaglio dell'utilizzo di questa neo-nata API.

 
Perche' Java Sound API?

E' lecito porre la domanda in quanto gia' esiste Java Media Framework che si occupa di gestire formati multimediali. Diciamo che laddove sia necessaria la sola esecuzione di files multimediali e' senz'altro sufficiente Java Media Framework, mentre dovunque siano richieste funzionalita' piu' avanzate come elaborazione, registrazione e mixaggio di audio digitale la Java Sound API trova la sua ideale collocazione. Fra i formati audio supportati ricordiamo : MIDI, WAV, AU,AIFF.L'elaborazione audio in generale richiede un grosso lavoro  a livello di processore, e la maggior parte dei sistemi di sviluppo audio sono fortemente basati su codice specifico per la piattaforma utilizzata. Java Sound utilizza invece uno strato di astrazione che isola la specificita' architetturale dal lavoro dello sviluppatore,
che diventa cosi' realmente "platform-independent". Per di piu', Java Sound si pone come 
potenziale servizio a supporto di altre tecnologie Java. Ad esempio le Java 3D API, con  l'introduzione del suono tridimensionale, o lo stesso Java Media Framework, per il quale
Java Sound puo' mettere a disposizione il proprio timer, permettendo la sincronizzazione di
fonti multimediali diverse, come suono e immagine, fino alla Java Speech API, per la quale e'
richiesta l'interazione con microfono e cuffie.
 

Architettura dell'API

In un sistema audio su computer possiamo distinguere dispositivi di due tipi: hardware e 
virtuali. I primi possono essere: lettori di CD, schede audio, microfoni etc., mentre fra i
secondi rientrano in generale dei moduli software, come ad esempio un programma mixer, un  lettore MIDI,un sintetizzatore software etc. Piu' dispositivi virtuali possono utilizzare lo
stesso dispositivo hardware, quindi ad esmpio mixer e amplificatore software utilizzeranno
per l'output la stessa scheda sonora. Ad un dispositivo hardware sono associate una o piu'
porte che possono essere di input (microfono etc.) o di output (cuffie etc.). I dispositivi
virtuali infine utilizzano dei canali multimediali per comunicare con il dispositivo hardware.
E fin qui' potrebbe sembrare solo teoria molto astratta. In realta' ogni oggetto che abbiamo 
nominato ha un suo oggetto corrispondente nella Java Sound API; cosi' un dispositivo hardware e' un SystemDevice, un dispositivo virtuale e' un ControlDevice, una porta e' una Port e un canale multimediale e' un MediaChannel. 
 

Altri oggetti

L'oggetto base per la sincronizzazione e' javax.media.Time . Questo oggetto supporta una
precisione fino al nanosecondo. Esistono poi altre classi di Timer per i dati MIDI e per i
campioni audio. E' facile comprendere quanto l'accuratezza di questo oggetto sia importante,
soprattutto nel caso in cui si presenti la necessita' di intervenire su campioni di suono 
registrati, o nel caso si debbano sincronizzare sorgenti multimediali diverse.
Il trasporto dei dati viene effettuato mediante javax.media.Buffers. Un Buffer e' un contenitore generico per diversi tipi di dati, siano essi : suono, video o testo. Vengono gestiti due modelli di trasporto : "pull" e "push". In generale possiamo dire che il primo viene utilizzato per l'elaborazione del suono, il secondo per la cattura di campioni.
Per quanto riguarda la gestione degli eventi distinguiamo:
- TransportEvents, generati quando cambia lo stato di un oggetto implicato nel trasporto di dati
- AudioControlEvents, riguardanti lo stato dei controlli audio-specifici come : guadagno,
volume etc. 
- FormatChangeEvents, legati ad oggetti per i quali subentri una modifica del formato di dati
trasportati.
Infine e' indispensabile introdurre l' AudioManager, che e' l'oggetto di ingresso nella Java
Sound. Attraverso di esso si ottiene l'accesso ai vari SystemDevices e ControlDevices.
Al solito, il modo migliore per vedere in dettaglio le caratteristiche di questo tipo di
prodotti e' la pratica, quindi vediamo ora passo passo le fasi necessarie alla costruzione di
un'applicazione per l'esecuzione di un brano MIDI o RMF. Al momento la Java Sound supporta i
file MIDI di tipo 0 e 1. Ho preso come esempio l'applicazione MidiFileApp distribuita come
esempio insieme alla Java Sound, trascurando tutta la gestione grafica e degli eventi, in modo
da focalizzare l'attenzione sulla Java Sound API.
I package da importare sono tutti sotto javax.media, e nel nostro caso bastano i seguenti:
 
 
 
 
 

import javax.media.*; 

import javax.media.format.*;

import javax.media.format.midi.*;

import javax.media.transport.*; 

import javax.media.sound.*; 

import javax.media.sound.midi.*; 
in piu' avremo bisogno di: 
 

import java.io.*; 

import java.net.*; 

per la gestione dei file e per l'oggetto URL.
 

public class ProvaMidi  { 
 
 

        SequencePlayer channel   = null; 

Per tutti gli errori che potranno essere generati lanciamo una MediaException, che verra'
gestita nel "main", che crea l'istanza della classe ProvaMidi. In questo modo semplifichiamo
la gestione degli errori. Questo e' possibile perche' nel nostro caso, un errore genera sempre
lo stesso tipo di comportamento, cioe' la visualizzazione di una stringa di errore, e l'uscita
dal programma.
        public ProvaMidi(String file) throws MediaException { 
Creiamo ora un FileInputStream da associare ad un MediaStream per la lettura del file MIDI.
 

        MediaStream mediaStream; 

        InputStream is = null;
 
 

        try { 
 
 

                URL url = new URL(file);

                is = url.openStream();
 
 

        } catch (Exception e) {
 
 

                try {
 
 

                        is = new FileInputStream(file); 
 
 

                } catch (Exception e2) {
 
 

                        throw new MediaException("Impossibile accedere al file : "

                                                   + file);

              }

        }
 
 
 

Costruiamo un MediaStream per il file MIDI. Notiamo che il MediaStream e' creato 
nell'AudioManager, che e' il punto di accesso alla Java Sound API.
Facciamo tutti i controlli sul file, e cioe' se esiste e se il suo formato e' valido.

 

        try { 
 
 

                mediaStream = AudioManager.newMediaStream(is);
 
 

        } catch (Exception e) { 
 
 

                throw new MediaException("Controllare file "+ file +

                                            " sia valido" ); 

        } 
 
 
 
 

        if ( ! (mediaStream.getFormat() instanceof MidiFileFormat) ) {
 
 

                throw new MediaException("Not a MIDI file: " + 

                mediaStream.getFormat().toString());

        }
 
 
 

Creiamo ora sul dispositivo virtuale MIDI un "player" MIDI, che e' poi l'esecutore del brano.
 
 

        try { 
 
 

                MidiOutControlDevice controlDevice = 

                (MidiOutControlDevice)AudioManager.getControlDevice(Class.forName(

                     "javax.media.sound.midi.MidiOutControlDevice"), null); 

                channel = controlDevice.newSequencePlayer(); 
 
 

        } catch (Exception e) { 
 
 

                throw new MediaException("MidiOutControlDevice 

                                     non disponibile sul sistema"); 

        } 
 
 
 

Carichiamo la sequenza di dati MIDI nel "player" appena creato. 

        try { 
 
 

                channel.load(mediaStream); 
 
 

        } catch(Exception e) { 
 
 

                throw new MediaException("Controllare che il file "+ file + " 

                esista e sia valido");

        } 
 
 
 

Allochiamo le risorse per il MediaChannel e facciamo partire l'esecuzione del brano.

        try { 
 
 

                channel.acquire(); 

                channel.start();
 
 

        } catch (ResourceUnavailableException e) { 
 
 

                throw new MediaException("Dispositivo audio non disponibile"); 

        } 
 
 

 } 

Il metodo "main" si occupa di istanziare la classe ProvaMidi e di intercettare gli errori
generati.Il nome del brano (MIDI o RMF deve essere passato come parametro dalla riga comando. 

        public static void main( String[] args) {
 
 

                if (args.length == 0) {
 
 

                        System.err.println("\nE' necessario specificare un file 

                                     MIDI come parametro.\n");
 
 

                } else {
 
 

                        try {
 
 

                                ProvaMidi app = new ProvaMidi(args[0]);
 
 

                        } catch (MediaException e) {
 
 

                                System.err.println("Impossibile eseguire 

                                     l'applicazione: "  + e.getMessage());

                      }

                }

        }

 }

Per evitare di dover gestire degli eventi, cosa che avrebbe spostato la focalizzazione su altre
tematiche, nell'applicazione non viene gestito lo stop del brano. La cosa e' pero' molto
semplice. Infatti, una volta creato il "player" MIDI, che abbiamo chiamato "channel", esso e'
in grado di gestire tutti i metodi di controllo dell'esecuzione del brano. Cosi' ad esempio :

channel.stop(); // ferma l'esecuzione del brano.

channel.setDB(float gain); // stabilisce il guadagno in decibel.

channel.setPan(float pan); // stabilisce il panning (destra,sinistra,centro)

channel.fade(float gain,Time time) // porta il guadagno a "gain" nel tempo "time"

channel.setMute(boolean mute) // attiva/disattiva il "muto"

E cosi' via. Come si vede la quantita' di azioni intraprendibili e' notevole, malgrado ci
siamo soffermati solo sull'oggetto SequencePlayer. Questo puo' dare pero' un'idea di cosa 
sia possibile fare con questa potente API. Ribadiamo il fatto che la Java Sound API e' da 
considerarsi una API a basso livello, e come tale va ad interagire spesso con parametri
specialistici del suono, e quindi e' giustificabile il suo utilizzo laddove queste finezze 
siano richieste.
 

Stato dei lavori 

La versione testata e' la 0.8 Early Access. I formati audio supportati sono : WAVE, AIFF/AIFC,
AU, MIDI tipo 0 e 1, e RMF. Le piattaforme supportate sono : Solaris, Win-NT, Windows 95/98.
E' previsto il supporto per Solaris x86 nelle successive versioni. E' richiesto il JDK 1.1
oppure Java2.
Previste, ma non ancora implementate numerose caratteristiche importanti : MIDI input da
dispositivi esterni, esecuzione in loop di brani MIDI, gestione di Soundbank (banchi di suoni),
effetti comi il reverbero o il pitch. Sono presenti alcuni piccoli bug, ma se ne assicura
l'eliminazione nella versione ufficiale, la 1.0 . 
 

Conclusioni

Un solo articolo non poteva essere minimamente esaustivo, data la vastita' di oggetti
implementati nell'API; cio' che abbiamo tentato di fare e' stato di dare una panoramica
chiara sul tipo di prodotto presentato. Sarebbe stato interessante anche vedere il trattamento
dei file audio digitali e la cattura di audio, ma la struttura di base rimane quella mostrata,
quindi non dovrebbe essere difficile sperimentare le altre caratteristiche della Java Sound.
L'impressione suscitata dall'uso dell'API e' molto positiva. Innazitutto l'idea di base, cioe'
quella di fornire uno strumento di sviluppo a basso livello per le esigenze musicali piu'
avanzate non puo' che riscuotere il plauso degli addetti ai lavori, e poi lo sforzo fatto per
presentare da subito un prodotto piuttosto stabile e potente si nota immediatamente.
Aspettiamo ardentemente le versioni successive con le implementazioni delle caratteristiche
mancanti, per portare finalmente anche il sequencing sulla strada della "platform independence".


 
 

MokaByte Web  1999 - www.mokabyte.it

MokaByte ricerca nuovi collaboratori. 
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it