Anda di halaman 1dari 5

Python DESARROLLO

Aprovecha y usa cualquier libreras de Java.

JUKEBOX CON JYTHON

Damos otro pequeo avance con Jython: Este mes utilizamos libreras Java y analizamos la reproduccin de sonidos desde Java para crear un mini reproductor de MP3 donde reproducir nuestra msica. POR JOS PEDRO ORANTES Y JOS MARA RUZ
l formato de sonido MP3 es uno de los mas consolidados dentro de la msica. A diario miles de personas en todo el mundo comparten y crean millones de ficheros en MP3 y los reproductores de msica, ya sea en el ordenador, en los equipos de msica o para el coche, se han adaptado para reproducir este formato. Actualmente la mayora de los reproductores de MP3 desarrollados para PC (como xmms, etc..) soportan este formato de msica. Pero, cmo podemos crear nuestro propio reproductor de MP3? Programar un reproductor de sonido en Java, exceptuando que reproduzca los formatos comunes que soporta, es bastante complicado. Se necesita mucho tiempo para realizar el descodificador del formato (MP3 en este caso) que procese los datos (conocidos como stream de audio) que contiene cada cancin codificada en este formato. Por eso, lo mejor para poder crear nuestro reproductor, es utilizar libreras que ya estn preparadas para descodificar el stream y para que podamos reproducirlo. En este caso utilizaremos libreras bajo la licencia LGPL (Lesser General Public License).

En Java, en general, para reproducir un sonido es necesario definir varias partes: Obtener el stream de audio, descodificarlo, tratarlo y escribirlo en el dispositivo de nuestro PC.

Requisitos
Como requisitos fundamentales, vamos a necesitar nuestro querido intrprete de Jython. Para obtenerlo nos dirigimos a su web [1] y descargamos la ultima versin (versin 2.1 en el momento de escribir esto). Necesitamos la nueva versin de Java (JRE 1.5 o JDK 1.5), disponible en [3], ya que incorpora algunas novedades y mejoras que necesitaremos para nuestro pequeo reproductor de MP3. Por ultimo necesitamos obtener las libreras que vamos a utilizar, MPSPI que podemos encontrar en la web de Javazoom (ver [2]) y ms concretamente en http://www. Javazoom.net/mp3spi/mp3spi.html. Una vez instalados el interprete de Jython y el JDK 1.5 (o JRE 1.5) debemos proceder a instalar las libreras de Javazoom. Para ello, descomprimimos el fichero que contiene las libreras de Javazoom (ya sabis, unzip -x fichero.zip o bien tar xvfz fichero.tar.gz ) y, como en

la mayora de los casos, se nos crear un directorio nuevo con el contenido del fichero. El primer paso ser copiar las libreras que necesitamos al directorio lib/ext/ de nuestro JRE (Maquina virtual de Java). En nuestro caso, despus de instalar el JDK 1.5, este directorio se encuentra en /usr/Java/jdk1.5.0_02/jre/lib/ext/ y debemos copiar aqu (obviamente como root) los paquetes mp3spi1.9.2.jar, y dentro del directorio lib, jl1.0.jar y tritonus_share.jar. Una vez hecho esto, al arrancar el intrprete de Jython nos aparecer un mensaje por cada archivo que hemos copiado, indicando que dicho paquete se est procesando para su uso (justo antes de arrancar nuestro intrprete). Para confirmar que todo es correcto, en el interprete introducimos import Javazoom y no debera salir ningn mensaje de error (en caso de que as fuera, comprobad que los archivos estn copiados en el sitio correcto). Con estas libreras tenemos ahora un descodificador del stream de nuestros archivos MP3 adems de algunas otras utilidades (libreras para conversin de formatos, lector de la informacin de las

WWW.LINUX- MAGAZINE.ES

Nmero 07

61

DESARROLLO Python

Listado 1: jython-julebox.py
01 import org.tritonus.share.sampled.TAudioFormat as taf 02 import org.tritonus.share.sampled.file.TAudioFileFormat as ftaf 03 import javax.sound.sampled as sampled 04 import java.io as io 05 import jarray 06 import java.util as util 07 import javax.swing as swing 08 import java.lang as lang 09 import java.awt as awt 10 import java.util as util 11 12 class jyMusica(lang.Thread): 13 def __init__(self): 14 self.panelReproductor() 15 self.menu() 16 self.win = swing.JFrame("JyMusica", size=(150, 150),windowClosing=self.exit) 17 self.win.setJMenuBar(self.menu ) 18 self.win.contentPane.add(self. pnlBoton, awt.BorderLayout.NORTH) 19 self.win.contentPane.add(self. pnlBoton2, awt.BorderLayout.CENTER) 20 self.win.show() 21 self.win.setResizable(0) 22 self.win.pack() 23 24 def exit(self, event): 25 lang.System.exit(0) 26 27 def run(self): 28 archivo = io.File(self.nombre) 29 inp = sampled.AudioSystem.getAudioIn putStream(archivo) 30 baseFileFormat = sampled.AudioSystem.getAudioFileF ormat(archivo) 31 baseFormat = baseFileFormat.getFormat() 32 self.getTag(baseFileFormat) 33 self.player.play() 34 35 36 37 38 39 40 41 42 43 self.txtArea.setText("") 44 self.txtArea.append("\nAuthor: ") 45 self.txtArea.append(val + "\n") 46 self.txtArea.append("title: ") 47 self.txtArea.append(tag +"\n") 48 self.txtArea.append("Album: ") 49 self.txtArea.append(alb + "\n") 50 51 52 53 54 55 56 def panelReproductor(self): self.pnlBoton = swing.JPanel(awt.FlowLayout()) self.pnlBoton2 = swing.JPanel(awt.FlowLayout()) acciones = ["Play", "Stop"] self.txtArea = swing.JTextArea(6,18) self.areaSrl = swing.JScrollPane(self.txtArea ) def getTag(self, baseFileFormat): properties = ftaf.properties(baseFileFormat) key = "author" val = properties.get(key) key = "title" tag= properties.get(key) key = "album" alb = properties.get(key) "Stop"] 63 self.menu = swing.JMenuBar() 64 archivo = swing.JMenu("Archivo") 65 for eachOpcion in opciones: 66 archivo.add(swing.JMenuItem(ea chOpcion, actionPerformed=self.accionMen u)) 67 archivo.addSeparator() 68 self.menu.add(archivo) 69 70 def accionMenu(self, event): 71 self.accion = event.getActionCommand() 72 if self.accion == 'Play': 73 selector = swing.JFileChooser() 74 rtn = selector.showOpenDialog(self.w in) 75 archivo = selector.getSelectedFile() 76 self.player = pad.AdvancedPlayer(io.FileInpu tStream(archivo)) 77 self.nombre=archivo.toString() 78 self.play() 79 80 elif self.accion=='Stop': 81 self.stop() 82 83 def play(self): 84 self.hilo = lang.Thread(self) 85 self.hilo.start() 86 87 def stop(self): 88 self.player.stop() 89 # self.line.drain() 90 # self.line.stop() 91 # self.line.close() 92 # self.din.close() 93 # self.hilo.interrupt() 94 # self.hilo.finalize() 95 # 96 97 if __name__=='__main__': 98 jyMusica()

57 self.pnlBoton.add(self.areaSrl ) 58 for cadaBoton in acciones: 59 self.pnlBoton2.add(swing.JButt on(cadaBoton, actionPerformed=self.accionMen u)) 60 61 def menu(self): 62 opciones = ["Play",

62

Nmero 07

WWW.LINUX- MAGAZINE.ES

Python DESARROLLO

canciones -artista, lbum, etc.- y algunas un array de 4096 Bytes que crearemos applets. Una de las limitaciones de CLIP, cosas ms). usando jarray.zeros(4096,'b'). Si quisiraes que necesita cargar el sonido entero en Nos falta un ltimo paso: Debemos elimos que nuestro array fuera de Strings, memoria antes de reproducirlo lo cual minar una de las limitaciones de Jython. haramos un jarray.zeros(45,Java. aumenta los recursos utilizados, adems Java tiene distintos tipos de lang.String). En caso de que necesitrade que slo est preparada variables, mtodos, consmos (que, de hecho, necesitamos) pasar para reproducir los formatos tructores, etc., que pueden como argumento la longitud de nuestro tpicos en Java (Wav, A-Law, ser privados (private) o array a un mtodo, haramos len(array) U-Law, etc.). Nosotros utilipblicos (public). Es imporya que los arrays de Jython no tienen un zaremos un SourceDataLine tante saber que en Jython no mtodo como .length() de los arrays de y trabajaremos con un se pueden declarar variables, Java. pequeo Buffer que llenaremtodos, etc., como pblicas Por ultimo, crearemos el mos con el stream de sonido Figura 1: Aqu tenemos ni como privadas y cuando SourceDataLine y desde su mtodo descodificado que luego a nuestro pequeo intentamos acceder, por write() escribiremos nuestro sonido en introduciremos en nuestro reproductor de MP3. ejemplo, a una interfaz absnuestro dispositivo de audio (entonces ya dispositivo de sonido. tracta privada, nos saltar un mensaje de empezaremos a or msica) y luego bastaLos pasos que seguiremos para tratar el error diciendo que no es posible acceder a ra con rellenar el buffer (desde nuestro sonido sern los siguientes. Primero, la interfaz abstracta con el denominador AudioInputStream) y volver a escribirlo obtendremos el Stream y el formato de public. en nuestro dispositivo de sonido hasta sonido (utilizando AudioSystem y sus Solventar esta limitacin es sencillo. que lleguemos al final de la cancin o mtodos getAudioInputStream, y Basta con editar el fichero de registro de hasta que paremos la reproduccin. getAudioFileFormat). Adems, del formajython que se encuentra en el direcFinalmente, y dado que vamos a usar to de audio (AudioFormat) leeremos los torio d o n d e h a y a m o s i n s t a l a d o un pequeo interfaz para manejarnos datos del MP3 (el ttulo, el lbum, el j y t h o n ( e n nuestro caso $HOME/ (hecho con swing), autor, etc). Desarrollo/jython-2.1/registry) y busnecesitaremos prograUna vez obtenido camos la lnea python. mar Java con hilos todo esto (el descodifisecurity.respectJavaAccessibility = true y (Thread), por lo que cador ya esta haciendo cambiamos true por false. Ya tenemos nuestro reproductor su trabajo), necesitarenuestro sistema preparado. heredara de la clase mos crear el buffer, Por ltimo, es muy importante tener a Thread y en el que no es otra cosa mano la API (documentacin) de Java momento de reproduque un array de Bytes. disponible tambin en su web y la de las cir, se lanzar un hilo Para crear dicho array. Figura 2: Este es el selector de libreras que vamos a utilizar. De que ser el encargado Jython nos provee de ficheros (JFileChooser) que se lanJavazoom deberis bajar la documentade reproducir la msiun modulo llamado zar al ejecutar su mtodo cin de JLayer (la de MPSPI viene en el ca mientras seguimos jarray que tiene dos showOpenDialog(). fichero que bajasteis al principio) y la API teniendo el control en constructores: zeros y de las libreras tritonus la podis bajar de nuestro pequeo reproductor. Un Thread array. El primero crea un array inicializa[4] (estn al final de la pgina). es una secuencia de instrucciones que se do a 0 del tamao y del tipo que nosotros ejecuta en paralelo con la aplicacin que necesitemos y el segundo crea un array ya Sonido en Java? lo invoca, podramos decir que mientras inicializado (por nosotros) de un tipo de nosotros usamos nuestra aplicacin, landatos. El sonido de Java requiere que los prograzamos otra pequea aplicacin que reproVeamos una ejemplo del uso de Jarray. madores conozcan como funcionan sus ducir nuestro sonido sin interferir en la clases principales. Las que vamos a utiliprimera (ejecucin en paralelo). zar son AudioSystem, AudioFormat y >>> import jarray SourceDataLine. AudioSystem est conec>>> a = jarray.zeros(4,'i') Mtodos y Libreras tada a los dispositivos del sistema de >>> print a forma que podemos acceder a ellos adearray([0, 0, 0, 0], int) Vamos a implementar un total de once ms de que dentro de ella tenemos mtomtodos. Empezaremos describiendo el >>> b = jarray.array([1,2,3,4],U dos para tratar el stream de audio. motor del reproductor y terminaremos 'i') AudioFormat con el que manejaremos el describiendo los de creacin del interfaz. >>> print b formato de audio que necesitaremos. Y Son cuatro mtodos. array([1, 2, 3, 4], int) SourceDataLine que nos proporciona la Run(). Desde este mtodo es de entrada al dispositivo de sonido, es decir, donde comienza el proceso del Como podis ver el funcionamiento es que podremos introducir nuestro sonido tratamiento de nuestro fichero sencillo. Jarray.zeros recibe dos argumenen el dispositivo y de esta forma escuMP3. Desde aqu obtendremos tos que son el numero de elementos y el charlo). el InputStream y el AudioFormat tipo de datos, en este caso 'i' son enteros Java proporciona una clase ms directa y una vez que los tengamos, (integer). Y jarray.array recibe la secuenque es CLIP. Suele utilizarse ms para pasaremos a llamar al siguiente cia que forma el array y el tipo de datos sonidos pequeos en aplicaciones o en mtodo (Reproducir() ha de llatambin. Para nuestro buffer necesitamos

WWW.LINUX- MAGAZINE.ES

Nmero 07

63

DESARROLLO Python

marse run() ya que es una implementacin del mtodo run() que heredamos de la clase Thread. De esta forma al invocar el mtodo start() del hilo nuevo, directamente ejecutara el mtodo run(). Reproducir(). Aqu es donde creamos el buffer de Bytes y el SourceDataLine. De esta forma, recibimos el AudioInputStream (el stream de audio) y crearemos el SourceDataLine (que recibimos desde el mtodo getLine que definiremos un poco ms adelante). Luego entraremos en un bucle en el que contaremos los bytes que lemos y los bytes que escribamos, y desde el que leeremos (llenando el buffer) desde el AudioInputStream y luego lo escribiremos en el dispositivo de sonido desde el SourceDataLine. Finalmente, cuando se termina la cancin, cerraremos el SourceDataLine y el AudioInputSource. GetLine(). Desde este mtodo creamos el SourceDataLine (desde AudioSystem) a partir del stream ya descodificado y nos lo devolver. Usaremos un mtodo que implementa el JDK1.5 que es el getSourceDataLine(), de esta forma nos evitamos hacer downcasting desde el mtodo getLine() de AudioSystem (que nos devuelve un DataLine.info). Finalmente tenemos getTag(). Este mtodo ser el encargado de devolvernos la informacin de la cancin (autor, titulo, etc). Recibir el formato del fichero sin descodificar, creara una ocurrencia de tipo Map desde la cual accederemos a los datos que mas nos interesan, en este caso, lbum, titulo y autor. Para los mtodos de la interfaz (fig1), tendremos el creador del botn y del cuadro de texto (panelReproductor()) desde el que crearemos los botones (JButtons) y el rea de texto (JTextArea()) que nos mostrar la informacin del MP3, el creador del men (menu()) que crear un men con dos opciones (play y stop) que realizarn las mismas funciones que los botones, el recogedor de los eventos del ratn (accionMenu()) que ejecutar los meto-

dos Play y Stop, el mtodo Play() que iniciara el hilo que reproducir la cancin, adems de lanzar el selector de ficheros y el mtodo Stop() que detendr la reproduccin en curso. La interfaz es muy simple, una rea de texto (donde se insertarn los datos del MP3) el botn Play que abrir un selector de ficheros (fig2) desde el que seleccionaremos el fichero a reproducir y el botn Stop que detendr la reproduccin de la msica. Cada vez que pulsemos en Play tendremos que seleccionar de nuevo la cancin a reproducir. En vosotros queda la idea de mejorarlo aadindole una lista de reproduccin, botn para pasar a la siguiente cancin y a la anterior, etc. En el nmero 4 de Linux Magzine, vimos el uso de otros componentes como JList y almacenar datos en ficheros, con eso sabris ponerle una lista de reproduccin a nuestro player. Para reducir el tamao de la aplicacin hemos omitido algunos puntos como recoger las excepciones, esto lo podris notar cuando se abra el selector de ficheros y pulsis el botn cancelar, abris un fichero que no contiene MP3, etc. Deberis depurarlo. Y por ltimo, el mtodo __init__() tpico en las aplicaciones Python y Jython que inicializar los valores de la aplicacin creando el panel contenedor de la interfaz grfica, e inicializar la variable booleana desde la que se controlar la parada del reproductor (Stop) y el mtodo exit() que permitir a nuestra aplicacin salir cuando hagamos clic en el botn de cerrar la ventana (la tpica X arriba a la derecha). Otro punto que debemos comentar es el trato que le da Jython a las variables booleanas ya que no existen los valores true y false. Para solucionar este problema, en vez de true utilizamos el valor 1 y en vez de false utilizaremos el 0, que son los valores equivalentes para Java. Las libreras que vamos a utilizar son las siguientes: org.tritonus.share.sampled.TAudioFormat y org.tritonus .share.sampled.file.TAudioFileFormat que vamos a usar para obtener la informacin de nuestro MP3. Javax.sound.sampled aqu dentro estn nuestras preciadas clases SourceDataLine, AudioSystem, etc. Java.io para el acceso a ficheros. jarray que nos permite crear arrays compatibles con Java, y por ultimo Java.util, Javax.swing, Java.lang, Java.awt,

Java.util que nos servirn para el resto de nuestras tareas. En caso de que quisiramos reproducir canciones situadas en Internet abriramos la ubicacin creando una ocurrencia URL (deberemos aadir la librera Java.net, a la cual le pasamos el protocolo que vamos a usar, el host al que tiene que conectarse, el puerto de conexin y el nombre del fichero. Una vez que tenemos preparada la ubicacin, el objeto URL con el mtodo openStream() empieza a extraer el stream de ese fichero, ya solo tenemos que pasrselo a nuestro reproductor (desde el get.AudioInputStream()) y al poco rato (dependiendo de la velocidad de conexin y el tamao) empezaremos a escuchar msica. Todo el programa se puede ver en el Listado 1.

Otro Reproductor es Posible


S, s que tenemos una forma mas fcil de crear un reproductor. El cometido de este artculo era ver como podamos crear, a partir de los elementos que tenemos de Java, un reproductor segn nuestras necesidades (como el tamao del buffer). En las libreras que hemos instalado de Javazoom, en JLayer concretamente, existe una clase que crea un reproductor de MP3 completo al que nicamente hemos de pasarle el InputStream (con FileInputStream) cuando lo creamos, y al ejecutar el mtodo .play() (Fig3) de dicho reproductor y l se encargar del resto. Y El mes que viene volveremos con ms scripts en Python y Jython. s

RECURSOS
[1] http://www.jython.org [2] http://www.Javazoom.net [3] http://Java.sun.com [4] http://www.tritonus.org [5] Descargas de magazine.es http://www.linux-

Jos Pedro Orantes Casado cursa 3 de Ingeniera Tcnica en Informtica de Sistemas y desde hace varios aos utiliza linux como herramienta de trabajo y como sistema de escritorio. Jose Mara Ruz est realizando el Proyecto Fn de Carrera de Ingeniera Tcnica en Informtica de Sistemas y lleva mas de 7 aos usando y desarrollando Software Libre, y desde hace 2 en FreeBSD

64

Nmero 07

WWW.LINUX- MAGAZINE.ES

LOS AUTORES