Sonido en JAVA

Sonido en JAVA.

Historial del documento.

Versión

Autor

Resumen de la modificación.

Fecha

1.0.0.

CPV

Guía para el programador de sonido con Java.

19-12-2000

Autores del documento.

CPV:

Carlos Prades del Valle.

e-mail: mailto:cprades@eresmas.com

Sitio web: http://cprades.eresmas.com/

Resumen.

Este documento es una guía para el programador que quiere empezar a utilizar el sonido que le proporciona el sistema multimedia que utiliza.

Palabras relacionadas.

Java, programación, sonido, procesado, voz, señal, api.

Índice.

Historial del documento. 2

Índice. 3

Glosario. 4

Ámbito y alcance del documento. 5

Convenciones del documento. 6

1 Introducción. 7

2 Clases principales. 8

2.1 Formatos de audio. 8

2.2 Arquitectura de los sistemas. 9

2.3 Mixers. 9

2.4 Lines. 10

2.5 Clases DataLine. 12

3 Acceso a los componentes del sistema. 13

3.1 Obtención de los recursos. 14

4 Reproducción de sonido. 16

4.1 Reproducción del sonido mediante SourceDataLine. 16

4.2 Sincronización de líneas. 18

4.3 Captura de sonido. 19

4.4 Procesado de la señal. 20

Bibliografía. 22

Glosario.

API: Application Program Interface. Es la interfaz proporcionada por un sistema al programador para poder acceder a los servicios de ese sistema.

Array: Es un grupo de datos de un tipo determinado puestos uno a continuación de otro. Coincide con los tipos de datos array de Java.

Java: Lenguaje de programación orientado a objetos.

JDK: Java Development Kit. Es el entorno de desarrollo para Java proporcionado por Sun.

MIDI: Estándar para el almacenamiento y transporte de música para o desde un sintetizador.

Package: Paquete. Agrupamiento especificado por el programador de clases con características comunes.

Sun: Empresa norteamericana que desarrolló el lenguaje de programación Java.

Thread: Hilo de ejecución.

Ámbito y alcance del documento.

Este documento pretende mostrar cómo utilizar las herramientas del API de Java para la adquisición el manejo y la reproducción de sonido, el texto se limita a esta función quedando fuera del propósito el almacenamiento, la síntesis de sonido, el tratamiento de secuencias MIDI, el tratamiento (reconocimiento y síntesis) de la señal de voz, etc. No es una descripción detallada de todos las clases, métodos y variables que proporciona el sistema sino una guía útil para el programador que pretenda iniciarse en el procesado de sonido con Java o cómo texto de referencia para el programador más experto.

La versión de Java que soporta este API como aquí se cuenta es la 2 y el entono de desarrollo utilizado es el JDK 1.3.

Este documento presupone que el lector tiene un conocimiento medio sobre el lenguaje Java siendo recomendable un conocimiento básico del API más común. Por otro lado el lector deberá tener un conocimiento mínimo sobre las características de la señal de sonido y el tratamiento digital de la señal.

Convenciones del documento.

En este documento se intentará traducir los términos en inglés siempre que sea posible, exceptuando aquellos términos en inglés que, por el uso común en nuestro idioma, no necesitan tal traducción. Estos vocablos están escritos en cursiva. Por otro lado los nombres propios de las compañías comerciales o de las marcas de sus productos también son puestos en cursiva.

La parte de código y los ejemplos están escritos con fuente de letra Curier. Cuando se habla de una clase o de un objeto se considera un nombre propio por lo que no se traduce, aún así en determinados casos se especifica la traducción de forma aclaratoria. Los nombre de paquetes y ficheros también se consideran nombres propios, pero al no pertenecer exclusivamente al código se muestran con fuente de letra normal en cursiva (como productos software que son).

1 Introducción.

El API de Java dedicada al sonido es la llamada Java Sound API proporcionado ya con el entorno de desarrollo de Sun. Este API se compone de 4 packages (paquetes):

javax.sound.sampled.

javax.sound.sampled.spi.

javax.sound.midi.

javax.sound.midi.spi.

El primero, javax.sound.sampled, contiene las clases necesarias para el manejo del sonido muestreado, esto incluye la captura, la mezcla y la reproducción de audio, proporcionando además algún control y efecto sobre el sonido así como interfaces para el almacenamiento, será el package estudiado en este documento. El package javax.sound.midi proporciona las interfaces de síntesis, secuenciamiento y transporte MIDI. Los packages javax.sound.sampled.spi y javax.sound.midi.spi proporcionan una interfaz para los desarrolladores de servicios basados en las interfaces anteriores.

2 Clases principales.

Como paso previo es necesario contar cómo funcionan algunos de los objetos de javax.sound.sampled necesarios para comprender después cada uno de los procedimientos a seguir.

2.1 Formatos de audio.

Los objetos de la clase AudioFormat definen el formato de una señal de audio, esto es, el número de canales, el número de bits por muestra, frecuencia de muestreo, el tamaño de la trama de voz y su frecuencia, formato de almacenamiento de los datos en memoria (big endian o little endian) y el tipo de codificación (ley a, ley m, PCM). Para guardar el tipo de codificación hay un objeto de la clase AudioFormat.Encoding con esta información. Obteniendo este objeto se puede mirar si es igual (función boolean equals(Object) ) a una de las constantes definidas en la clase AudioFormat.Encoding (ALAW, ULAW, PCM_SIGNED, PCM_UNSIGNED). La clase AudioFormat, aparte del constructor (al que hay que pasarle los parámetros para la iniciación de las variables), tiene los siguientes métodos:

int getChanels(); /* Devuelve el número de canales. */ AudioFormat.Encoding getEncoding(); /* Devuelve objeto con información sobre el tipo de codificación. */ float getFrameRate(); /* Devuelve la frecuencia de trama de la señal. */ int getFrameSize(); /* Devuelve tamaño de la trama en bytes. */ float getSampleRate(); /* Devuelve la frecuencia de muestreo. */ int getSampleSizeInBits(); /* Devuelve el tamaño en bits de cada muestra de sonido. */ boolean isBigEndian(); /* Devuelve true alineamiento big endian y false little endian. */

Además de los métodos boolean maches(AudioFormat) y String toString() y los heredados de la clase Object.

Por otro lado existe la clase AudioFileFormat para definir el formato de un fichero de audio, esta clase permite utilizar las clases AudioImputStream y AudioOutputStream, que heredan de ImputStream y OutputStream respectivamente, permitiendo usar los métodos estándar para la lectura y escritura del audio en ficheros. El funcionamiento de estas clases se escapa al ámbito de este documento.

2.2 Arquitectura de los sistemas.

La arquitectura de los sistemas de audio de audio se basa en mezcladores, clase Mixer, que son dispositivos a los que llegan líneas, objetos de clase Line, procesan los datos y salen otros objetos de clase Line.

La jerarquía de estas clases es la siguiente:

Object    Line        Port        Mixer        DataLine            SourceDataLine            TargetDataLine            Clip

2.3 Mixers

Como se puede ver los Mixer (mezcladores) son unos casos particulares de los Lines. Estos Mixer son dispositivos hardware o software por lo que pueden ser proporcionados por el sistema. Los objetos Mixer contienen un objeto de la clase Mixer.Info con información del tipo de Mixer. Los métodos de la clase Mixer son los siguientes:

Line getLine(Line.Info); /* Obtiene, si exixte, un Line del tipo indicado en el parámetro. */ int getMaxLines(Line.Info); /* Indica el numero de lineas que se pueden tener de un tipo dado. */ Mixer.Info getMixerInfo(); /* Obtiene el objeto que indica el tipo de Mixer que es. */ Line.Info [] getSourceLineInfo(); /* Obtiene array con información de los SourceLines disponibles. */ Line.Info [] getSourceLineInfo(Line.Info); /* Array con información de los SourceLines de un tipo. */ Line [] getSourceLines(); /* Obtiene array con los SouceLines disponibles. */ Line.Info [] getTargetLineInfo(); /* Obtiene array con información de los TargetLines disponibles. */ Line.Info [] getTargetLineInfo(Line.Info); /* Array con información de los TargetLines de un tipo. */ Line [] getTargetLines();      /* Obtiene array con los TargetLines disponibles. */ boolean isLineSupported(Line.Info); /* Devuelve true si tiene un Line del tipo especificado. */ boolean isSincronizationSupported( Line[], boolean); /* Devuelve true si se soporta sincronización entre las lineas especificadas en el primer parámetro, el segundo parámetro indica el tipo de sincronización: muestra a muestra si es true o sólo en los metodos start() y stop() si es false. */ void sincronize(Line[], boolean); /* Sincroniza las lineas especificadas en el primer parámetro, el segundo parámetro indica el tipo de sincronización: muestra a muestra si es true o sólo en los metodos start() y stop() si es false. */ void unsincronize(Line[]); /* Desincroniza las lineas indicadas. */

2.4 Lines.

Un Line (línea) es una conexión por la que pasa la señal desde o hacia un Mixer. Los tipos de Lines son los siguientes:

Port: Puertos de entrada o salida del sistema como el micrófono, la salida de línea, el altavoz, etc.

DataLine: Líneas de datos, pueden ser Clips que almacenan un sonido completo, SourceDataLine que proporcionan un buffer de entrada a un Mixer y TargetDataLine que proporcionan el buffer de salida de un Mixer.

Mixer: Mezclador que representa un dispositivo hardware o software del sistema.

Los Lines proporcionan cierta funcionalidad al sistema por medio de objetos Control que incluyan capaces de variar alguna característica del sonido (ganancia, reverberación, etc.), del estado (el Status) que puede ser abierto y cerrado (Open y Closed) de tal manera que si el Line está cerrado no consume recursos del sistema y por último por medio de los objetos Event (eventos) lanzados que permiten comunicación y sincronización con otros objetos.

Como ya veremos los objetos Line tienen un objeto (de clase Line.Info) de información sobre ellos. Como ocurre con la mayor parte de los atributos y métodos, el objeto de información se redefine en los objetos que lo heredan siendo de tipo Mixer.Info, Port.Info, etc. en cada uno de los casos.

Los métodos de la clase Line son:

void addLineListener( LineListener); /* Especifica un objeto al que se le envían los Event generados. */ void close(); /* Cierra (pasa a Status Closed) el objeto Line. */ Control getControl( Control.Type); /* Devuelve un objeto Control del tipo especificado. */ Control [] getControls(); /* Devuelve un array con los objetos Control disponibles. */ Line.Info getLineInfo(); /* Devuelve información del objeto. */ boolean isControlSupported(Control.Type); /* Devuelve true si soporta el tipo de Control especificado. */ boolean isOpen(); /* Devuelve true si el Status es Open. */ void open(); /* Abre (pasa a Status Open) el objeto Line. */ void removeLineListener(LineListener); /* Deja de enviar objetos Event al LineListener especificado. */

2.5 Clases DataLine.

La los objetos de la clase DataLine son las conexiones entre los Mixer y nuestro sistema. Estos objetos tienen un buffer de datos, un proceso de control interno, y una interfaz con el usuario. Como es lógico heredan todos los métodos de la clase Line pero además implementan los siguientes:

int available(); /* Indica el número de bytes que que están libres en el buffer interno. */ void drain(); /* Este método sirve bloquea el objeto hasta que quede limpio el buffer. */ void flush(); /* Este metodo limpia el buffer. */ int getBufferSize(); /* Devuelve el tamaño del buffer en bytes. */ AudioFormat getFormat(); /* Devuelve el formato del audio. manejado. */ int getFramePosition(); /* Devuelve la posición de la trama. */ float getLevel(); /* Devuelve el nivel de la señal. */ long getMicrosecondPosition(); /* Obtiene la posición actual de los datos de audio en microsegundos. */ boolean isActive(); /* Devuelve true si estan pasando datos (ejecutado el comando start) */ boolean isRunning(); /* Devuelve true si está abierto. */ void start(); /* Comienza la reproducción. */ void stop(); /* Termina la reproducción. */

3 Acceso a los componentes del sistema

Para acceder a los componentes del sistema la clase AudioSystem proporciona a la aplicación un punto de entrada a los componentes instalados en el sistema. Se puede obtener información de los objetos Mixer instalados y objetos Line (sin tener que especificar el Mixer al que están asociados), proporciona métodos para realizar las conversiones de formato y métodos para trasladar el sonido a objetos Stream o File para el transporte, comunicación o almacenamiento. Los métodos accesibles de esta clase son:

static AudioFileFormat getAudioFileFormat(java.io.File); static AudioFileFormat getAudioFileFormat(java.io.InputStream); static AudioFileFormat getAudioFileFormat(java.net.URL); /* Devuelven un objeto con el formato del fichero especificado en el parámetro. */ static AudioFileFormat.Type[] getAudioFileTypes(); static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream); /* Devuelve los tipos de ficheros soportados por el sistema (sin parámetro) o por el parámetro. */ static AudioInputStream getAudioInputStream(AudioFormat, AudioInputStream ); static AudioInputStream getAudioInputStream(AudioFormat.Encoding, AudioInputStream); /* Devuelve un AudioImputStream */ static AudioInputStream getAudioInputStream(java.io.File); static AudioInputStream getAudioInputStream(java.io.InputStream); static AudioInputStream getAudioInputStream(java.net.URL); /* Devuelve un AudioInputStream asuciado a un fichero static Line getLine(Line.Info); /* Obtiene un objeto Line del tipo especificado en el parámetro. */ static Mixer getMixer(Mixer.Info); /* Obtiene un objeto Mixer del tipo especificado en el parámetro. */ static Mixer.Info[] getMixerInfo(); /* Obtiene un array con la información de los objetos Mixer existentes en el sistema. */ static Line.Info[] getSourceLineInfo(Line.Info p1); /* Obtiene un array con la información de los objetos Line existentes en el sistema. */ static AudioFormat.Encoding[] getTargetEncodings(AudioFormat); static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding); /* Obtiene los tipos de codificación sopurtados para un determinado formato de audio. */ static AudioFormat[] getTargetFormats(AudioFormat.Encoding, AudioFormat); /* Obtiene los objetos AudioFormat de un determinado tipo. */ static Line.Info[] getTargetLineInfo(Line.Info); /* Obtiene información de los objetos TargetLine de un determinado tipo. */ static boolean isConversionSupported(AudioFormat, AudioFormat); static boolean isConversionSupported(AudioFormat.Encoding, AudioFormat); /* Devuelve true si se permite la canversión entre los formatos especificados. */ static boolean isFileTypeSupported(AudioFileFormat.Type); static boolean isFileTypeSupported(AudioFileFormat.Type, AudioInputStream); /* Devuelve true si se soporta el tipo de formato de fichero (para el AudioInputStream en el segundo caso). */ static boolean isLineSupported(Line.Info); /* Devuelve true si en el sistema existe un objeto Line del tipo especificado. */ static int write(AudioInputStream, AudioFileFormat.Type, java.io.File); static int write(AudioInputStream, AudioFileFormat.Type, java.io.OutputStream); /* Graba los datos de audio en un fichero o en un OutputStream según el formato especificado. Devuelve el número de datos guardados. */

Para poder acceder a los distintos objetos del sistema se crean las clases de información, cuyas instancias proporcionan información sobre las distintas interfaces. Estas interfaces son Line.Info y las clases derivadas Mixer.Info, Port.Info y DataLine.Info.

3.1 Obtención de los recursos.

Para obtener un objeto de la clase Mixer se puede conseguir un array de objetos Mixer.Info con todos los Mixer soportados por el sistema por medio del método getMixerInfo() de la clase AudioSystem. Con este array podemos elegir el Mixer que nos interese y obtenerlo con el método getMixer( Mixer.Info) de la clase AudioSystem.

Para obtener un objeto Line de un determinado tipo podemos obtenerlo de un Mixer (ver apartado dedicado a los objetos Mixer) o de la clase AudioSystem con el método getLine(Line.Info). Se puede construir un objeto DataLine.Info indicando en el constructor la clase del objeto del que informa (TargetDataLine.class o SourceDataLine.class) y un objeto de la clase AudioFormat. Es conveniente ver si un objeto Line del tipo deseado se soporta por el sistema, para ello se utiliza el método isLineSupported() de la clase AudioSystem antes de intentar obtenerlo. La clase Port.Info proporciona unas constantes (instancias final static de clase Port.Info) que definen algunas líneas básicas (Port.Info.COMPACT_DISC, Port.Info.HEADPHONE, Port.Info.LINE_IN, Port.Info.LINE.OUT y Port.Info.SPEAKER).

Para obtener un array con información de las líneas existentes en el sistema se usan las funciones getTargetLineInfo() y getSourcetLineInfo(), de la clase AudioSystem, pasándoles los tipos de líneas que nos interesan. Por otro lado, los objetos Mixer implementan los métodos getTargetLineInfo() y getSourcetLineInfo() que no necesitan parámetros y que devuelven información sobre sus objetos Line. Con el método getLine(), del objeto Mixer, pasando como parámetro el Line.Info adecuado se obtiene la referencia al objeto Line deseado.

Con estos datos es posible obtener un Line y manipularlo abriéndolo, cerrándolo, etc. Hay que advertir que no es aconsejable cambiar el Status de los Ports ya que un usuario puede tener abierto o cerrado estos puertos por conveniencia molestándole si la aplicación los cambia de estado sin su consentimiento.

4 Reproducción de sonido.

Antes de entrar en la reproducción de sonido por medio de objetos SourceDataLine hay que hacer mención as los objetos Clip. Un objeto de esta clase está pensado para almacenar el audio grabado de principio a fin, es la mejor solución cuando se conoce de antemano el tamaño de la señal a almacenar y es única, así como cuando se desea repetir un sonido varias veces (por ejemplo en un lazo). Los ejemplo típico de utilización es la reproducción de sonido de ficheros no demasiado grandes, en este caso se lee el contenido del fichero en un clip y luego se reproduce, el control es más sencillo y los recursos utilizados son menores. Sin embargo en los casos donde el audio es continuo o en ficheros de un gran tamaño es preferible usar los otros tipos de DataLine (SourceDataLine o TargetDataLine) con el fin de no ocupar demasiada memoria del sistema. En nuestro caso nos centraremos en este segundo caso ya que nuestra fin es la adquisición tratamiento y reproducción de la señal siéndonos de poca utilidad la clase Clip.

4.1 Reproducción del sonido mediante SourceDataLine

Los objetos SourceDataLine son la entrada de objetos Mixer, siendo necesario escribir en ellos los datos que se introducen en el Mixer. Los métodos de estos objetos son:

void open(AudioFormat); void open(AudioFormat, int); // Redefinen el metodo de abir de Line pasando el formato de audio // que van a manejar y, opcionalmente, el tamaño en bytes del buffer. int write(byte [ ], int, int); // Escribe los datos en el buffer (ver texto a continuación).

El proceso a realizar para la escritura de datos en el SourceDataLine una vez obtenido es el siguiente:

Se procede a abrirlo con el método propio open(AudioFormat) o open(AudioFormat, int) donde el entero indica el tamaño del buffer en bytes, si no se utilizan argumentos se pone un formato por defecto. Para conocer estos datos se pueden utilizar los métodos getFormat() y getBufferSize() del objeto SourceDataLine.

Con el método start() la línea empezará a reproducir sonido en cuanto tenga algo en el buffer. Para poner datos en el buffer se usa el método int write(byte [] b, int offset, int length) donde b es el array de datos, offset indica a partir de donde, en el array b, se debe empezar a leer datos y length indica cuantos datos deben ser leídos. Esta función devuelve el número de datos leídos. Es muy importante tener en cuenta que todos estos datos vienen en bytes y no en número de muestras.

Cuando sale la primera muestra del objeto Mixer (un instante después de salir del SourceDataLine) se produce un evento de tipo START que puede ser recogido por el proceso a la salida del Mixer.

El método write() vuelve sólo cuando ha escrito todos los datos en el buffer. Si se intenta escribir más datos de los que caben en el buffer el método se bloquea hasta terminar. Para evitarlo con el método available() se obtiene, en bytes, el tamaño de la parte que queda libre en el buffer.

Con el método drain() el programa se bloquea hasta reproducir el sonido, vaciando el buffer antes de volver. Con el método stop() se para la reproducción, sin limpiar el buffer, y con start() continúa dónde se quedó, para evitar la reproducción de un segmento antiguo al llamar a start() se puede utilizar el método flush() que limpia el buffer. Todos estos métodos de SourceDataLine son heredados de DataLine.

Cuando deja de salir señal del Mixer procedente del SourceDataLine se genera un evento de tipo STOP.

El método isActive() devuelve true si están saliendo datos (entre los eventos de tipo START y STOP), el método isRunning() devuelve true si la línea está abierta. Además Line genera eventos de tipo OPEN y CLOSE al llamarse a las funciones correspondientes. Todos los eventos pertenecen a la clase LineEvent y les debe de atender un objeto que implemente la interfaz LineListener. Para registrarlos se llama a la función de la clase Line, addLineListener(), pasando como parámetro el objeto LineListener.

La interfaz LineListener sólo define un método: void update(LineEvent). Los objetos LineEvent implementan los métodos:

final long getFramePosition(); // Obtiene la osicion de la trama. final Line getLine(); // Obtiene el objeto Line que lanzó el evento. final Line Event.Type getType(); // Obtiene el tipo de evento. LineEvent(Line, LineEvent.Type, long); // Constructor. java.lang.String toString(); // Obtiene un String para la representación.

El tipo de evento es un objeto de clase LineEvent.Type, esta clase tiene definida las constantes LineEvent.Type.CLOSE, LineEvent.Type.OPEN, LineEvent.Type.START y LineEvent.Type.STOP.

4.2 Sincronización de líneas.

La sincronización de líneas permite que la reproducción se realice al mismo tiempo, esta sincronización puede ser mantenida o no. La sincronización se llama mantenida cuando es una sincronización muestra a muestra, es decir, durante toda la reproducción las muestras de ambas líneas salen a la par. La sincronización no mantenida es aquella en la que sólo se sincronizan los procesos de start() y stop().

Para ver si varios objetos Line se pueden sincronizar se utiliza el método, del un objeto Mixer, isSinchronizationSupported()( Line [], boolean) al que se le pasa un array con los objetos a sincronizar y un boolean que indica si la sincronización es mantenida (valor true) o no (valor false), como es lógico devuelve true si se pueden sincronizar y false si no.

4.3 Captura de sonido.

Tras lo expuesto anteriormente en el apartado de reproducción vale con una pequeña referencia al apartado de captura de sonido ya que todo se hace prácticamente igual.

En la captura de audio los Ports (puertos) ponen datos en el Mixer y este en un objeto TargetDataLine que tiene los métodos:

void open(AudioFormat); void open(AudioFormat, int); // Redefinen el metodo de abir de Line pasando el formato de audio que //van a manejar y, opcionalmente, el tamaño en bytes del buffer. int read(byte [], int, int); // Lee los datos desde el buffer (ver texto a continuación).

Aparte de los métodos heredados. Así se puede observar la cantidad de datos en el buffer con available() y leerlos con read().

Lo primero es obtener un objeto TargetDataLine (antes es necesario DataLine.Info) y abrirlo con open(), indicando, si es necesario, el formato de audio (si no, pone uno por defecto) y el tamaño del buffer. Para leer los datos de TargetDataLine hay que utilizar el método read() donde el primer parámetro es el array de bytes donde dejar los datos, el segundo es el offset a partir del que se dejan los datos en el array y el tercero indica el número de datos a leer.

Para limpiar el buffer se utiliza el método drain().

Esta clase genera los mismos eventos que los que hemos visto en el caso anterior LineEvent, con los mismos valores como LineEvent.Type (START, STOP, OPEN y CLOSE).

4.4 Procesado de la señal.

Una vez capturada la señal y/o antes de la reproducción tenemos la señal en un array, momento que podemos aprovechar para el procesado de la señal, en el caso de un proceso que no sea en tiempo real este array puede ser almacenado para el procesado posterior o el procesado por parte de otro thread del programa.

Existe la posibilidad de usar los el procesado soportado por los Mixer o los DataLine mediante el uso de los objetos de clase Control, esto permite utilizar funciones básicas de estos objetos como la ganancia (gain) o la reverberación (reverb). Todo objeto Line puede implementar este procesado. Los objetos Control en un Mixer pueden afectar sólo a algunos Line asociados a él. La jerarquía de estas clases es:

Control     BooleanControl     FloatControl     EnumControl     ComponentControl

Cada clase hija de Control tiene un una clase Type incluida que define constantes para cada tipo de control. Para obtener un Control de un Line se puede usar el método de ese Line getControl(Control.Type) que devuelve la instancia del Control, si queremos ver que controles soporta cada Line podemos llamar al método de Line getControls() que devuelve un array con todos los objetos Control soportados. Para saber que tipo de Control es el método del Control getType() devuelve un objeto de clase Control.Type y con el método de Object getClass() se puede saber la clase derivada a la que pertenece el objeto. Para cambiar los parámetros de un objeto Control se utiliza el método setValue() el tipo del parámetro de este método depende de la clase del método.

Bibliografía.

[1]                   Java Sound API Programmer’s Guide [ver 1.0]. Sun Microsystems, Inc. 1999-2000.

[2]                   Java Sound 1.0 API Specification. Sun Microsystems, Inc. http://java.sun.com/j2se/1.3/docs/guide/sound/.

[3]                 Java Sound Home Page.  Diciembre 2000. http://java.sun.com/products/java-media/sound/index.html.