Manager
Player e latenza del suono
La riproduzione di file audio richiede l'uso delle classi
javax.micredition.media.Manager e javax.microedition.media.Player.
Manager è un produttore di oggetti Player. Player
è l'oggetto che concretamente si occuperà
di generare, attraverso la piattaforma hardware, il
suono. Data la cronica scarsità di risorse dei
dispositivi CLDC, l'acquisizione delle risorse hardware
da parte di un player è rinviata fintantochè
il programma non invochi il metodo prefetch. In altri
termini, la costruzione di un oggetto Player non comporta
l'acquisizione di risorse e non è sufficiente
a preparare il suono alla riproduzione. Non è,
per un videogioco, una questione marginale. Il suono
segue l'azione, non può arrivare in ritardo.
Allo scopo di ridurre il periodo di latenza iniziale,
le MMAPI forniscono un Player del metodo prefetch. La
preparazione di un player segue dunque questi passi:
InputStream
source = getClass().getResourceAsStream("/suono.wav");
Player player = Manager.createPlayer(source, "audio/x-wav");
player.prefetch(); //preparazione
Una
volta che abbia avuto accesso alle risorse hardware,
il Player passerà allo stato PREFETCHED. In questa
condizione, il suono è pronto per essere riprodotto
in tempo reale. Quando il suono immagazzinato nel player
risulti ancora necessario, ma non nell'immediato, le
risorse hardware da questo occupate potranno essere
liberate, attraverso l'invocazione del metodo deallocate.
Un player per cui sia stato invocato il metodo deallocate
è ancora valido, e si trova nello stesso stato
precedente all'invocazione del metodo prefetch. Quando
il suono non sia più necessario per il prosieguo
del programma, tutte le risorse occupate dal Player
potranno essere liberate attraverso il metodo close.
Una volta chiuso, l'oggetto Player non è più
usabile.
Riproduzione
Un Player riproduce il suono quando sia invocato il
metodo start. Documentazione alla mano, start restituisce
il controllo dopo la produzione di un evento STARTED.
L'evento in questione è intercettabile da eventuali
oggetti PlayerListener connessi al Player. La restituzione
del controllo non implica l'istantanea riproduzione
del suono. In altri termini, l'invocazione del metodo
start non garantisce che il controllo sia restituito
quando il player sia passato allo stato STARTED. Tuttavia,
la previa invocazione del metodo prefetch è tutto
quanto sia possibile fare per garantire una corretta
riproduzione. Cerchiamo di esemplificare la riproduzione
condizionata ad un evento. Definiamo come evento la
pressione di un tasto. Nulla vieta che l'evento sia
altro (ad esempio una collisione).
import
javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.media.*;
import java.io.*;
public
class Sample extends MIDlet implements PlayerListener
{
Canvas canvas = new MyCanvas();
Player player;
public Sample() {
try {
InputStream source
= getClass().getResourceAsStream("/spacemusic.wav");
player = Manager.createPlayer(source,
"audio/x-wav");
player.addPlayerListener(this);
} catch(Exception e) {
e.printStackTrace();
}
}
public void startApp() {
try {
player.prefetch();
} catch(MediaException e) {
e.printStackTrace();
}
Display.getDisplay(this).setCurrent(canvas);
}
public void pauseApp() {
player.deallocate();
}
public void destroyApp(boolean u) {
player.close();
notifyDestroyed();
}
public void startSound() {
try {
player.start();
} catch(MediaException e) {
e.printStackTrace();
}
}
public void stopSound() {
try {
player.stop();
player.setMediaTime(0);
} catch(MediaException e) {
e.printStackTrace();
}
}
public void playerUpdate(Player p, String
event, Object eventData) {
if(event == PlayerListener.STOPPED
|| event == PlayerListener.STARTED) {
canvas.repaint();
}
}
}
private
class MyCanvas extends Canvas {
protected
void paint(Graphics g) {
g.setColor(player.getState()
== Player.STARTED ? 0xFFFF00 : 0);
g.fillRect(0, 0,
getWidth(), getHeight());
}
protected void keyPressed(int code) {
if(code == KEY_NUM0) {
startSound();
}
else
if(code == KEY_NUM1) {
stopSound();
}
}
}
Il
costruttore di Sample si occupa di creare un oggetto
Player. Per rappresentare a video lo stato del Player,
è aggiunto un PlayerListener. Si tratta di un'interfaccia
essenziale, a cui Player notifica alcuni mutamenti di
stato. Tra queste notifiche, catturiamo il momento in
cui player entra in stato STARTED e STOPPED. Nel primo
caso, coloriamo lo schermo di giallo, nel secondo useremo
il nero (metodo paint della classe interna MyCanvas).
Invochiamo prefetch nel momento in cui l'oggetto MIDlet
è attivato. Liberiamo le risorse allocate con
prefetch nel momento in cui l'oggetti MIDlet sia interotto
(metodo pause). La pressione del tasto 0 (zero) invoca
il metodo startSound che, a sua volta, gira la richiesta
al player. La richiesta ha effetto solo se il suono
non sia già in riproduzione. La pressione del
tasto 1 invoca stop. Il metodo stopSound ferma il souno
e azzera il tempo di esecuzione. Senza azzeramento,
il Player conserverebbe la posizione del cursore di
riproduzione. La successiva invocazione di start riprenderebbe
il suono dal punto in cui si era fermato.
Conclusioni
Quanti suoni è possibile eseguire, contemporaneamente,
su un dispositivo CLDC? Dipende dal dispositivo. La
piattaforma software è in grado di caricare un
numero non definito di suoni, ognuno incapsulato in
un oggetto Player. Quanti, dipende dalle risorse di
sistema. Di questi suoni precaricati, solo alcuni possono
passare allo stato PREFETCHED. L'eccezione MediaException,
eventualmente rilasciata durante l'esecuzione del metodo
prefetch, indica esattamente questa momentanea indisponibilità
di risorse hardware. Esiste poi la questione dei Thread
dedicati alla riproduzione audio. Nelle piattaforme
J2SE è la norma. I suoni sono eseguiti nutrendo
un pool di N processi con uno o più buffer di
dati audio. Per i PC, è una questione di semplicità
d'uso, e non solo di prestanzioni. Per i processori
CLDC, quantomeno gli attuali, è preferibile sfruttare
l'asicronia propria del metodo start, senza ulteriori
processi.
Bibliografia
[1] Sun Microsystem - "JSR-135 JCP Specifications",
http://www.jcp.org/aboutJava/communityprocess/final/jsr135/
[2] Sun Microsystem - "JSR-118 JCP Specifications",
http://jcp.org/aboutJava/communityprocess/final/jsr118/index.html
[3] Brackeen et al. - "Developing Games in Java",
New Riders, 2002
|