viernes, 4 de enero de 2013

Poner sonido a nuestras aplicaciones Java (Funciona en versión 1.7)

Buenas amigos, en este nuevo tema vamos a comentar como ponerle sonido a nuestra aplicación java. Se nos proporciona la API  java Sound para ejecutar sonidos en nuestra aplicación. Esta API nos proporciona 4 paquetes en la que están introducidas todas las clases que nos harán falta:

*Antes de seguir leyendo, si lo que buscas es una solución a la excepción IOException mark/reset not support, que se produce al ejecutar el sonido de tu aplicación, dirígete al final del post.

    • javax.sound.sampled;//Proporciona las clases e interfaces para capturar, procesar y reproducir los datos de audio
    • javax.sound.sampled.spi;//Proporciona interfaces para desarrollar aplicaciones basadas en el paquete anterior.
    • javax.sound.midi;//Proporciona las interfaces y las clases necesarias para la E/S, secuencia y sintesis de archivos MIDI
    • javax.sound.midi.spi;//Proporciona interfaces para desarrollar aplicaciones basadas en el paquete anterior.
Nosotros vamos a trabajar con algunas clases del paquete javax.sound.sampled, mas concretamente con la "Interfaz Clip", la "clase AudioSystem" y la "Interfaz DataLine".

Interfaz Clip:

Representa una linea especial de datos audio, los cuales pueden ser cargados durante la reproducción , en vez de ser tratados en tiempo real.

¿Esto que quiere decir?Bien, esto os dice que los datos al ser cargados previamente a su reproducción, permite a la JVM conocer la longitud, lo cual nos permite a su vez poder reproducir nuestro audio en cualquiera de sus posiciones. Dicho mas coloquialmente, si hiciésemos un reproductor de audio, esta clase nos permitiría avanzar o retroceder en su reproducción.

Los métodos más interesantes que nos podemos encontrar son:
    • int getFrameLength(): Método que nos devuelve un número entero en representación del valor de la longitud de los frames del audio.
    • Long getMicrosecondLength(): Método que nos devuelve el tiempo de reproducción en microsegundos.
    • void loop(int n): Método que repite la reproducción del audio 'n' veces. Podemos ponerlo en un loop infinito de la siguiente manera: loop(Clip.LOOP_CONTINUOUSLY);
    • void open(AudioInputStream ais): Método que nos abre el clip de audio y los flujos presentes en formato de la entrada de audio.
No están todos, hay más, pero estos son los más importantes.

Interfaz DataLine:

Esta interfaz se encarga de iniciar, parar, drenar y limpiar los datos que pasan por la linea. También se encarga de realizar las conexiones entre nuestros "mixers" (Mezclador que representa un dispositivo hardware o un software del sistema).

Los métodos más utilizados por esta interfaz son:
    • void start(): Método que nos permite comenzar la reproducción del archivo.
    • void stop(): Método que nos permite detener la reproducción del archivo.
    • void drain(): Método que nos permite detener el archivo hasta que el buffer quede limpio.
    • void flush(): Método que nos permite limpiar el buffer.
    • int available(): Método que nos permite conocer el número de bytes que están libres en el buffer.
    • AudioFormat getFormat(): Método que devuelve el formato de audio.
    • int getBufferSize(): Método que nos devuelve el tamaño del buffer en bytes.
    • boolean isRunning(): Método que nos indica si la linea de audio esta abierta.

Hay unos cuantos métodos más, pero estos son los que se suelen utilizar más.

Clase AudioSystem:

Esta clase actúa como entrada a los recursos del sistema de muestreo de audio. Nos permite consultar o acceder a los mezcladores del sistema (tanto de hardware como de software), hacer conversiones de formato a los datos de audio y traducir entre archivo de audio y cadena. Dicho de otro modo, es la clase que nos permite leer los archivos de audio desde el pc.

Los métodos más utilizados de esta clase son:
  • static AudioFileFormat getAudioFileFormat(File f): Método que nos permite obtener el formato de archivo de audio del "File" pasado por argumentos. 
  • static AudioFileFormat getAudioFileFormat(Stream s): Método que nos permite obtener el formato de archivo de audio de un flujo
  • static AudioFileFormat getAudioFileFormat(URL u): Método que nos permite obtener el formato de archivo de audio desde una dirección URL o desde Internet.
  • static AudioInputStream getAudioinputStream(File f): Método que nos permite obtener un flujo de entrada por medio del "File" pasado por argumentos.
  • static AudioInputStream getAudioinputStream(InputStream s): Método que nos permite obtener un flujo de entrada por medio de una entrada o flujo.
  • static AudioInputStream getAudioinputStream(URL u): Método que nos permite obtener un flujo de entrada por medio de una dirección URL o desde Internet.
Bien, ya le hemos dado un pequeño repaso a las clases que más nos interesa conocer, si alguien está más interesado en conocer más estos paquetes, puede pinchar aquí

Ha llegado el momento de poner todo el entresijo de métodos a trabajar para que nos luzca en nuestra aplicación. Voy a crear una frame con un botón el cual hará "clic" cuando se pulse.

Lo primero de todo es descargarnos un sonido .wav de esta página: http://www.findsounds.com/types.html
Elegís el que más os luzca, hay una sección donde os podéis descargar un tipo llamado "clic" que viene perfecto para nuestro ejemplo.

Una vez descargado tenemos que introducirlo en nuestro proyecto:
  • Pinchamos con el botón derecho del ratón encima de la carpeta "src" del proyecto, en el menú que se despliega pincháis en "Nuevo" y posteriormente elegís "carpeta", le ponéis el nombre que deseéis, yo para este tema le pondré de nombre "sonido"
  • Ya tenemos nuestra carpeta creada, ahora debemos agregarle un sonido, para ello pinchamos  sobre la carpeta creada (os tiene que salir con un símbolo parecido al de paquete, pero en color blanco) y le dais a la opción de "importar"

  • Se nos abrirá una ventana con muchas carpetas, en este momento solo nos interesa la carpeta "General", pincháis en ella y se os desplegará otro menú donde debéis elegir la opción de "Sistema de archivos"

  • Se os abrirá una ventana donde deberéis elegir el directorio donde está el sonido, que os aparecerá en el cuadrado izquierdo, y en el derecho os saldrá todos los archivos que tenéis en ese directorio, solamente elegís vuestro sonido y le dais a "Finalizar". Fijaos antes de darle a "Finalizar" que la carpeta de destino sea la del proyecto.



Siguiendo estos pasos ya tenemos nuestro sonido en nuestra carpeta. 

Bien, realmente hacer que nuestro botón haga clic son solo 3 lineas de código:
        1. Obtenemos el Clip
        2. Lo abrimos
        3. Lo ejecutamos.
*Este método funciona en la versión 1.6 y anteriores, no sirve para la versión 1.7, si quiere ejecutarlo en versión 1.7 vaya al final del tema.

Bien, creamos un instancia de la interfaz Clip y llamamos al método getClip() de AudioSystem, que nos devuelve como tipo de retorno un clip:
      • Clip sonido=AudioSystem.getClip();
Ahora vamos a utilizar el método open(), el cual nos pide un AudioInputStream, para ello utilizamos el método getAudioInputStream(), al cual, le deberemos indicar donde se encuentra el InputStream, o flujo de datos, que deseamos que utilice, como este se encuentra dentro de nuestro proyecto debemos utilizar la siguiente linea de código para indicarle la ruta: getClass().getResourceAsStream(), OJO, no utilizar getResource(), ya que lo que queremos es que nos lo pase a Stream, lo cual solo hace el anterior y ya por último le agregamos la url de nuestro archivo "/sonidos/clic.wav". Con lo que nuestra linea de código quedaría así:
  • sonido.open(AudioSystem.getAudioIputStream(getClass().getResourceAsStream("/sonido/clic.wav");
Ya tenemos la comunicación abierta, solo nos falta reproducirla, ello lo hacemos con el método .start():
      • sonido.start();
Una cosa que no se ha dicho es que Clip hereda de DataLine, por esa misma razón puede utilizar los métodos de esta. También habrá que capturar algunas excepciones, como las que tengo yo capturadas, os lo pedirá el IDE.

Ya tenemos un método que reproduce un sonido, el código final nos quedaría así:

Como veis, creo en la frame un botón que cuando se pulsa ejecuta el método sonido(), el cual nos reproduce un sonido desde nuestro proyecto.

Bueno, hasta aquí todo, dicho de sobra es, que teniendo ya el método que nos reproduce el sonido podemos utilizarlo para cualquier tipo de evento...botones, checkbox, eventos de raton, etc...

Un saludo a todos.
*********************************************************************************
18/02/2013

Bien amigos, voy a editar este tema, ya que me dí cuenta de que en la versión de java 1.7 este código para escuchar sonido no funciona, compila bien, y mientras lo ejecutéis con vuestro IDE os funcionará, el problema reside al exportar el .jar, ya que, una vez realizado nuestro .jar y ejecutemos, nos mostrará la excepción IOException mark/reset not support. ¿Esto que nos dice? pues, simple y llanamente que no puede cargar el sonido porque no se puede refrescar.

Bien, para combatir el berrinche de Java, vamos a cargar previamente el sonido en el buffer para posteriormente ser pasado a la clase AudioSystem procesado por el sistema. Nos quedaría así:



La única diferencia que tiene con el método anterior es que le pasamos previamente el buffer, por lo demás  todo igual.

*En el ejemplo la URL está mal pone: ("sonidos/clic.wav") cuando debería de poner ("/sonidos/clic.wav"), no os confundáis que os salta la excepción IOException Stream Close....

Un saludo.

10 comentarios:

  1. pana.. me sale el error IOException mark/reset not support. en la reproduccion de mi audio .wav...

    ResponderEliminar
    Respuestas
    1. Buenas Junior, no se si terminaste de leer el post, pero al final comento como solucionar el mark/reset, un saludo.

      Eliminar
  2. Saludos man me arroja una excepción en un archivo .wav

    javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 44100.0 Hz, 24 bit, stereo, 6 bytes/frame, little-endian not supported.

    no hay otra herramienta que soporte más tipos y frecuencias de audio??

    ResponderEliminar
  3. Muchas gracias por la correción de java 1.7 me estaba matando "literal" xD

    ResponderEliminar
  4. como puedo hacer que se active un sonido al momento de correr el programa pero con un celular realisado en java y applet

    ResponderEliminar
  5. Muchas gracias! me fue útil, pero tuve que buscar más información en la API de Java

    ResponderEliminar
  6. Estimado, desde ya muchas gracias por la atención.
    Quisiera saber si es posible que se reproduzca la lectura de una cadena de texto, lo mas cercano que encontré es lo siguiente pero no tengo idea de como usarlo.

    Audio audio = Audio.getInstance();
    InputStream sound = audio.getAudio("Hola Usuario XXXX", Language.SPANISH);
    audio.play(sound);

    ResponderEliminar
  7. Muchas graciasss, no tengo palabras. Estuve buscando por todos lados porque en las nuevas versiones de java no funcionaba el sonido en los exe o jar, y gracias al Buffered funciona. Sos mi idolo, una semana buscando esto.

    ResponderEliminar
    Respuestas
    1. Hola, me podrías decir como lo hiciste por favor. He intentado, pero no me reconoce el getClass

      Eliminar