Nuestro juego M1945 no tiene sonido. La raz�n para ello ha sido mantener la compatibilidad con los dispositivos con soporte MIDP 1.0, que es el m�s extendido por ahora. Desgraciadamente MIDP 1.0. no ofrece ninguna capacidad para reproducir sonidos, por lo que los fabricantes han creado APIs propias e incompatibles entre s�. A partir de la versi�n 2.0 de MIDP s� se ha a�adido soporte multimedia, aunque a�n no se han extendido demasiado estos dispositivos en el mercado. La API encargada del sonido se llama MIDP 2.0 Media API. Hasta este cap�tulo, todo lo expuesto es compatible con MIDP 1.0, incluido el juego M1945. Se ha optado, pues, por separar en un cap�tulo aparte lo concerniente al sonido para no romper esta compatibilidad.
La API multimedia esta compuesta por tres partes:
- Manager
- Player
- Control
La funci�n de la clase Manager es crear objetos de tipo Player. Un Player es un objeto capaz de reproducir un tipo de informaci�n multimedia, ya sea audio o video. Por lo tanto, el Manager debe generar un tipo diferente de Player seg�n la naturaleza de lo que queramos reproducir. Para utilizar estos objetos hemos de importar el paquete javax.microedition.media. Finalmente, la clase Control nos premite controlar y gestionar un objeto de tipo Player. Esta clase se encuentra en el paquete javax.microedition.media.control.
�Sonidos
Hay eventos en los juegos que generan sonidos, como una explosi�n o un disparo. Este tipo de sonido suelen ser samples digitales. El formato m�s habitual para almacenar estos sonidos es el formato WAV. La siguiente l�nea crea un Player para un archivo WAV.
Player sonido = Manager.createPlayer("http://www.dominio.com/music.wav");
Si queremos crear un Player para un objeto almacenado en nuestro archivo JAR, hemos de utilizar una corriente de entrada para leerlo. En este caso, hemos de indicarle al Manager que tipo de archivo es.
InputStream in = getClass().getResourceAsStream("/explosion.wav"); Player sonido = Manager.createPlayer(in, "audio/x-wav");
Debes capturar las excepciones IOException y MediaException para crear este Player. Para reproducir el sonido usamos el m�todo start() del Player.
try { sonido.start(); } catch (MediaException me) { }
�M�sica
Adem�s de samples, la API multimedia nos permite reproducir notas musicales (tonos). La forma m�s simple de reproducir un tono es utilizar el m�todo playTone() de la clase Manager.
try { Manager.playTone(ToneControl.C4, 100, 80); } catch (Exception e){}
Este m�todo tiene tres par�metros. El primero es la frecuencia del tono. En este caso hemos utilizado la constante ToneControl.C4, que es la frecuencia de la nota Do central. Otra constante interesante es ToneControl.SILENCE. El segundo par�metro es la duraci�n de la nota en milisegundos, y el tercero el volumen de reproducci�n.
Reproducir una melod�a con la ayuda del m�todo playTone() puede ser un trabajo algo arduo. Es por ello que la API multimedia nos ofrece otra forma de reproducir secuencias de notas. Una secuencia es un array de bytes con un formato muy concreto. El array se va rellenando con pares de bytes cuyo significado analizaremos con un ejemplo.
byte[] secuencia = { ToneControl.VERSION, 1, ToneControl.TEMPO, tempo, // comienzo del bloque 0 ToneControl.BLOCK_START, 0, // notas del bloque 0 C4,d, F4,d, F4,d, C4,d, F4,d, F4,d, C4,d, F4,d, // fin del bloque 0 ToneControl.BLOCK_END, 0, // inicio del bloque 1 ToneControl.BLOCK_START, 1, // notas del bloque 1 C4,d, E4,d, E4,d, C4,d, E4,d, E4,d, C4,d, E4,d, // fin del bloque 1 ToneControl.BLOCK_END, 1, // reproducir bloque 0 ToneControl.PLAY_BLOCK, 0, // reproducir bloque 1 ToneControl.PLAY_BLOCK, 1, // reproducir bloque 0 ToneControl.PLAY_BLOCK, 0, };
Podemos observar que la secuencia est� dividida en tres secciones bien diferenciadas. En la primera establecemos la versi�n (del formato de secuencia) y el tempo de la melod�a. Observa como la informaci�n se codifica en pares de bytes. El primero indica el atributo para el que queremos establecer un valor, y el segundo es el valor mismo.
En la segunda secci�n de la secuencia definimos bloques de notas. Las notas comprendidas entre ToneControl.BLOCK_END y ToneControl.BLOCK_START forman un bloque. Podemos definir tantos bloques como necesitemos. Dentro de un bloque, las notas van definidas en pares, cuyo primer byte es la nota y el segundo es la duraci�n. Finalmente, la tercera secci�n indica el orden de reproducci�n de cada bloque de notas.
El c�digo encargado de reproducir la secuencia es el siguiente.
Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR); p.realize(); ToneControl c = (ToneControl)p.getControl("ToneControl"); c.setSequence(secuencia); p.start();
Vamos a reunir en el siguiente ejemplo pr�ctico todo lo expuesto en el presente cap�tulo.
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.media.*; import javax.microedition.media.control.*; import java.io.*; public class Sonido extends MIDlet implements CommandListener { private Display display; private Form formulario; private Command exit; private Command wav, nota, secuencia; public Sonido() { display = Display.getDisplay(this); exit = new Command("Salir", Command.EXIT, 1); wav = new Command("WAV", Command.SCREEN, 2); nota = new Command("Nota", Command.SCREEN, 2); secuencia = new Command("Secuencia", Command.SCREEN, 2); formulario = new Form("Reproducir."); formulario.addCommand(exit); formulario.addCommand(wav); formulario.addCommand(nota); formulario.addCommand(secuencia); formulario.setCommandListener(this); } public void startApp() { display.setCurrent(formulario); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command c, Displayable s) { if (c == exit) { destroyApp(false); notifyDestroyed(); } else { if (c == wav) playWav(); if (c == nota) playNota(); if (c == secuencia) playSecuencia(); } } public void playWav() { try { // Abrir corriente de datos del archivo de sonido InputStream in = getClass().getResourceAsStream("/explosion.wav"); Player p = Manager.createPlayer(in, "audio/x-wav"); // comenzar reproducci�n p.start(); } catch (Exception e) { Alert alr = new Alert("Error", "No se pudo reproducir el sonido.", null, AlertType.ERROR); alr.setTimeout(Alert.FOREVER); display.setCurrent(alr, formulario); } } public void playNota() { try { // reproducir nota Manager.playTone(ToneControl.C4, 100, 80); } catch (Exception e){} } public void playSecuencia() { byte tempo = 30; byte d = 8; // Creamos las notas a partir del Do central byte C4 = ToneControl.C4;; byte D4 = (byte)(C4 + 2); byte E4 = (byte)(C4 + 4); byte F4 = (byte)(C4 + 5); byte G4 = (byte)(C4 + 7); byte silencio = ToneControl.SILENCE; byte[] secuencia = { ToneControl.VERSION, 1, ToneControl.TEMPO, tempo, // comienzo del bloque 0 ToneControl.BLOCK_START, 0, // notas del bloque 0 C4,d, F4,d, F4,d, C4,d, F4,d, F4,d, C4,d, F4,d, // fin del bloque 0 ToneControl.BLOCK_END, 0, // inicio del bloque 1 ToneControl.BLOCK_START, 1, // notas del bloque 1 C4,d, E4,d, E4,d, C4,d, E4,d, E4,d, C4,d, E4,d, // fin del bloque 1 ToneControl.BLOCK_END, 1, // reproducir bloque 0 ToneControl.PLAY_BLOCK, 0, // reproducir bloque 1 ToneControl.PLAY_BLOCK, 1, // reproducir bloque 0 ToneControl.PLAY_BLOCK, 0, }; try{ Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR); p.realize(); ToneControl c = (ToneControl)p.getControl("ToneControl"); c.setSequence(secuencia); p.start(); } catch (IOException ioe) { } catch (MediaException me) {} } }