Hola Habr! Le presento la traducción del artículo
"Sonido Java, Primeros pasos, Parte 1, Reproducción" .
Sonido en JAVA, Primera parte, El comienzo. Reproducción de sonido
Este es el comienzo de una serie de ocho lecciones que lo familiarizarán completamente con la API de Java Sound.
¿Qué es el sonido en la percepción humana? Esta es la sensación que experimentamos cuando un cambio en la presión del aire se transmite a las pequeñas áreas sensoriales dentro de nuestros oídos.
Y el objetivo principal de crear la API de sonido es proporcionarle medios para escribir código, lo que ayudará a transferir ondas de presión a los oídos del sujeto correcto en el momento correcto.
Tipos de sonido en Java:
- La API de sonido Java admite dos tipos principales de audio (sonido).
- Sonido digitalizado y grabado directamente como un archivo
- Grabar como un archivo MIDI. Muy distante, pero similar a la notación musical, donde los instrumentos musicales se tocan en la secuencia deseada.
Estos tipos son bastante diferentes en su esencia y nos concentraremos en el primero, ya que en la mayoría de los casos tratamos con sonido que necesita ser digitalizado para grabar desde una fuente externa a un archivo o viceversa para reproducir el sonido previamente grabado desde dicho archivo.
Vista previa
La API de Java Sound se basa en el concepto de
líneas y mezcladores.Siguiente:
Describiremos las características físicas y eléctricas de la representación analógica del sonido aplicada a un
mezclador de audio .
Pasaremos al escenario de la banda de rock inicial, que utiliza seis micrófonos y dos altavoces estéreo en este caso. Necesitamos esto para comprender el funcionamiento del mezclador de audio.
A continuación, observamos una serie de temas de Java Sound para programación, como líneas, mezcladores, formatos para datos de audio y más.
Entenderemos las relaciones existentes entre los objetos SourceDataLine, Clip, Mixer, AudioFormat y crearemos un programa simple que reproduzca audio.
A continuación le damos un ejemplo de este programa, que puede usar para grabar y luego reproducir el sonido grabado.
En el futuro, proporcionaremos una explicación completa del código del programa utilizado para este propósito. Pero de ninguna manera completamente en esta lección.
Ejemplo de código y consideración
Características físicas y eléctricas del sonido analógico.El propósito de nuestra lección es presentarle los conceptos básicos de la programación Java utilizando la API de sonido Java.
La API de sonido de Java se basa en el concepto de un mezclador de audio, que es un dispositivo comúnmente utilizado para reproducir sonido en casi cualquier lugar: desde conciertos de rock hasta escuchar CD en casa. Pero antes de embarcarse en una explicación detallada del funcionamiento del mezclador de audio, será útil familiarizarse con las características físicas y eléctricas del sonido analógico.
Mira la fig. 1
Vasya Pupyrkin empuja un discurso.
Esta figura muestra a Vasya haciendo un discurso usando un sistema conocido como de dirección amplia. Tal sistema típicamente incluye un micrófono, amplificador y altavoz. El propósito de este sistema es fortalecer la voz de Vasya para que pueda ser escuchado incluso en una gran multitud.
Bamboleo en el aireBrevemente, cuando Vasya habla, sus cuerdas vocales hacen que las partículas de aire vibren en su laringe. Esto conduce a la aparición de ondas de sonido, que, a su vez, hacen que la membrana del micrófono vibre y luego se convierta en vibraciones eléctricas de muy pequeña amplitud que simulan exactamente las vibraciones del sonido del original de Vasya. Un amplificador, como su nombre lo indica, amplifica estas vibraciones eléctricas. Luego llegan al altavoz, que realiza la conversión inversa de vibraciones eléctricas amplificadas en ondas de sonido muy amplificadas, pero que sin embargo repiten exactamente las mismas ondas generadas en las cuerdas vocales de Vasya Pupyrkin.
Micrófono dinámicoAhora echemos un vistazo a la Fig. 2, que muestra un diagrama esquemático de un micrófono llamado dinámico.
Fig. 2 circuito de micrófono dinámicoLas vibraciones sonoras afectan la membrana.La presión de las vibraciones sonoras actúa sobre una membrana flexible dentro del micrófono. Esto hace que la membrana vibre, mientras que las vibraciones de la membrana repiten las vibraciones de las ondas sonoras.
Bobina móvilUna bobina de alambre delgado está unida a la membrana del micrófono. A medida que la membrana oscila, la bobina también hace movimientos alternativos en el campo magnético del núcleo hecho de un fuerte imán permanente. Y como Faraday también estableció, surge una corriente eléctrica en la bobina.
Una señal eléctrica sigue la forma de las ondas sonoras.Por lo tanto, a partir de una corriente muy débil inducida en la bobina, se obtiene una señal eléctrica alterna, que repite la forma de ondas de sonido que actúan sobre la membrana del micrófono. Además, esta señal en forma de voltaje alterno se alimenta a la entrada del amplificador de la Fig. 1)
AltavozDe hecho, el principio de funcionamiento del altavoz repite el dispositivo de un micrófono dinámico, solo encendido en la dirección opuesta.
(Naturalmente, en este caso, los cables de bobinado son mucho más gruesos y la membrana es mucho más grande para garantizar el funcionamiento con una señal amplificada)

Las oscilaciones de la membrana del altavoz afectan las partículas de aire y crean potentes ondas de sonido. La forma de estas ondas repite exactamente la forma de las ondas sonoras de mucha menor intensidad creadas por las cuerdas vocales de Vasya. Pero la intensidad de las nuevas olas ahora es suficiente para garantizar que las vibraciones de sonido de Vasya lleguen a los oídos de las personas que se encuentran incluso en las filas traseras de una gran multitud.
Concierto de rockEn este momento, es posible que se pregunte, ¿qué tiene que ver todo esto con la API de Java Sound? Pero espere un poco más, estamos abriendo el camino a los conceptos básicos del mezclador de audio.
El circuito descrito anteriormente era bastante simple. Consistía en Vasya Pupyrkin, un micrófono, un amplificador y un altavoz. Ahora considere el circuito con la Fig. 4, que presenta el escenario preparado para el concierto de rock del grupo musical principiante.
Seis micrófonos y dos altavoces.En la fig. 4 seis micrófonos se encuentran en el escenario. Dos altavoces (altavoces) se encuentran a los lados del escenario. Cuando comienza el concierto, los artistas cantan o tocan música en cada uno de los seis micrófonos. En consecuencia, tendremos seis señales eléctricas, que deben amplificarse individualmente y luego alimentarse a ambos altavoces. Además de esto, los artistas pueden usar varios efectos especiales de sonido, por ejemplo, reverberación, que también deberán convertirse en señales eléctricas antes de aplicarlos a los altavoces.
Dos altavoces a los lados del escenario están diseñados para crear el efecto del sonido estéreo. Es decir, la señal eléctrica que proviene del micrófono ubicado en el escenario a la derecha debe caer en el altavoz ubicado también a la derecha. Del mismo modo, la señal del micrófono a la izquierda se debe alimentar al altavoz ubicado a la izquierda de la escena. Pero las señales eléctricas de otros micrófonos ubicados más cerca del centro del escenario ya deberían transmitirse a ambos altavoces en proporciones apropiadas. Y dos micrófonos justo en el centro deben transmitir su señal a ambos altavoces por igual.
Mezclador de audioLa tarea discutida anteriormente solo la realiza un dispositivo electrónico llamado mezclador de audio.
Línea de audio (canal)Aunque el autor no es un experto en mezcladores de audio, en su humilde entendimiento, un mezclador de audio típico tiene la capacidad de recibir en la entrada un cierto número de señales eléctricas independientes entre sí, cada una de las cuales representa la señal o línea de audio original
(canal).(El concepto de un canal de audio será muy importante cuando comencemos a comprender la API de Java Sound en detalle.
Procesamiento independiente de cada canal de audio.En cualquier caso, el mezclador de audio estándar tiene la capacidad de amplificar cada línea de audio independientemente de los otros canales. Además, el mezclador generalmente tiene la capacidad de imponer efectos especiales de sonido, como, por ejemplo, reverberación a cualquiera de las líneas de audio. Al final, el mezclador, como su nombre lo indica, puede mezclar todas las señales eléctricas individuales en los canales de salida como se establecerá, para controlar la contribución de cada línea de audio a los canales de salida. (Este control generalmente se llama pan o pan - distribución en el espacio).
Volviendo al sonido estéreoPor lo tanto, en el diagrama con la Fig. 4, el ingeniero de sonido del mezclador de audio tiene la capacidad de combinar señales de seis micrófonos para obtener dos señales de salida, cada una de las cuales se transmite a su altavoz.
Para una operación exitosa, la señal de cada micrófono debe ser suministrada en la proporción apropiada, dependiendo de la ubicación física del micrófono en el escenario. (Al cambiar el panorama, un ingeniero de sonido calificado puede cambiar la contribución de cada micrófono si es necesario, si, por ejemplo, el vocalista principal se mueve por el escenario durante un concierto).
Es hora de volver al mundo de la programación.Regresemos ahora del mundo físico al mundo de la programación. Según Sun:
“Java Sound no implica ninguna configuración de hardware especial; Está diseñado para permitir que varios componentes de audio se instalen en el sistema y se pongan a disposición del usuario a través de la API. Java Sound admite la funcionalidad de entrada y salida estándar de una tarjeta de sonido (por ejemplo, para grabar y reproducir archivos de audio), así como la capacidad de mezclar múltiples transmisiones de audio "Mezcladores y canalesComo ya se mencionó, la API de Java Sound se basa en el concepto de mezcladores y canales. Si te mueves del mundo físico al mundo de la programación, entonces Sun escribe lo siguiente con respecto al mezclador:
“Un mezclador es un dispositivo de audio con uno o más canales. Pero el mezclador que realmente mezcla la señal de audio debe tener varios canales de entrada de fuentes de origen y al menos un canal de salida de destino ".Las líneas de entrada pueden ser instancias de clases con objetos SourceDataLine, y las líneas de salida pueden ser objetos TargetDataLine. El mezclador también puede recibir sonido pregrabado y en bucle como entrada, definiendo sus canales de fuente de entrada como instancias de objetos de clase que implementan la interfaz Clip.
Interfaz de línea de canal.
Sun informa lo siguiente desde la interfaz de línea: “La
línea es un elemento de una tubería de audio digital, como un puerto de audio de entrada o salida, un mezclador o una ruta de audio hacia o desde un mezclador. Los datos de audio que pasan por el canal pueden ser mono o multicanal (por ejemplo, estéreo). ... Un canal puede tener controles, como ganancia, panorama y reverberación ".Poniendo los términos juntosEntonces, las citas anteriores de Sun denotan los siguientes términos
Fuente de datos
Targetgetataline
Puerto
Clip
Controles
Fig. 5 muestra un ejemplo del uso de estos términos para construir un programa de salida de audio simple.
Guion de programaDesde el punto de vista del software 5 muestra un objeto Mixer obtenido con un objeto Clip y dos objetos SourceDataLine.
¿Qué es el clip?Clip es un objeto en la entrada del mezclador, cuyo contenido no cambia con el tiempo. En otras palabras, carga los datos de audio en el objeto Clip antes de reproducirlo. El contenido de audio del objeto Clip se puede reproducir una o más veces. Puede hacer un loopback del Clip y luego el contenido se reproducirá una y otra vez.
Flujo de entradaEl objeto SourceDataLine, por otro lado, es un objeto continuo en la entrada del mezclador. Un objeto de este tipo puede recibir un flujo de datos de audio y enviarlo al mezclador en tiempo real. Los datos de audio necesarios se pueden obtener de varias fuentes, como archivos de audio, conexión de red o memoria intermedia.
Diferentes tipos de canalesPor lo tanto, los objetos Clip y SourceDataLine pueden considerarse como canales de entrada para el objeto Mixer. Cada uno de estos canales de entrada puede tener los suyos: panorámica, ganancia y reverberación.
Reproduce contenido de audioEn un sistema tan simple, Mixer lee datos de líneas de entrada, usa el control para mezclar señales de entrada y proporciona salida a uno o más canales de salida, como un altavoz, salida de línea, conector para auriculares, etc.
El Listado 11 muestra un programa simple que captura datos de audio de un puerto de micrófono, almacena estos datos en la memoria y luego los reproduce a través del puerto del altavoz.
Discutiremos solo la captura y reproducción. La mayor parte del programa anterior es crear una ventana y una interfaz gráfica para el usuario para que sea posible controlar la grabación y la reproducción. No discutiremos esta parte como ir más allá de la meta. Pero luego consideraremos la captura y reproducción de datos. Discutiremos perder en esta lección y capturaremos en la próxima. En el camino, ilustraremos el uso del canal de audio con la API de sonido Java.
Los datos capturados se almacenan en un objeto ByteArrayOutputStream.
Un fragmento de código proporciona la lectura de datos de audio de un micrófono y su almacenamiento como un objeto ByteArrayOutputStream.
El método, llamado playAudio, que comienza en el Listado 1, reproduce los datos de audio que fueron capturados y almacenados en el objeto ByteArrayOutputStream.
private void playAudio() { try{ byte audioData[] = byteArrayOutputStream. toByteArray(); InputStream byteArrayInputStream = new ByteArrayInputStream( audioData);
Listado 1Comenzamos con el código estándar.El fragmento de programa en el Listado 1 en realidad aún no está relacionado con Java Sound.
Su propósito es:
- Convierta datos previamente guardados en una matriz de tipo byte.
- Obtenga el flujo de entrada para una matriz de datos de bytes.
Necesitamos esto para que los datos de audio estén disponibles para su posterior reproducción.
Ir a la API de sonidoLa línea de código en el Listado 2 ya está relacionada con la API de Java Sound.
AudioFormat audioFormat = getAudioFormat();
Listado 2Aquí tocaremos brevemente el tema, que se discutirá en detalle en la próxima lección.
Dos formatos independientesMuy a menudo estamos tratando con dos formatos independientes para datos de audio.
Formato de archivo, (cualquiera) que contiene datos de audio (en nuestro programa aún no lo está, ya que los datos se almacenan en la memoria)
El formato de los datos de audio enviados es en sí mismo.
¿Qué es un formato de audio?Esto es lo que Sun escribe al respecto:
“Cada canal de datos tiene su propio formato de audio asociado con su flujo de datos. El formato (una instancia de AudioFormat) determina el orden de bytes de la secuencia de audio. Los parámetros de formato pueden ser el número de canales, la frecuencia de muestreo, el bit de cuantificación, el método de codificación, etc. Los métodos de codificación habituales pueden ser la modulación de código de pulso lineal del PCM y sus variantes ".Secuencia de bytesLa fuente de datos de audio es una secuencia de bytes de datos binarios. Hay varias opciones para organizar e interpretar esta secuencia. No comenzaremos a tratar todas estas opciones en detalle, pero discutiremos un poco el formato de audio que usamos aquí en nuestro programa.
Pequeña digresiónAquí dejamos el método playAudio por ahora y miramos el método getAudioFormat del Listado 2.
El método completo getAudioFormat se muestra en el Listado 3. private AudioFormat getAudioFormat(){ float sampleRate = 8000.0F; int sampleSizeInBits = 16; int channels = 1; boolean signed = true; boolean bigEndian = false; return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian); }
Listado 3Además de declarar variables inicializadas, el código en el Listado 3 contiene una expresión ejecutable.
AudioFormat ObjectEl método getAudioFormat crea y devuelve una instancia de un objeto de la clase AudioFormat. Esto es lo que Sun escribe sobre esta clase:
“La clase AudioFormat define el orden específico de los datos en una secuencia de audio. Volviendo a los campos del objeto AudioFormat, puede obtener información sobre cómo interpretar correctamente los bits en una secuencia de datos binarios ".Usamos el constructor más simple.La clase AudioFormat tiene dos tipos de constructores (tomaremos el más trivial). Los siguientes parámetros son necesarios para este constructor:
- Frecuencia de muestreo o frecuencia de muestreo por segundo (valores disponibles: 8000, 11025, 16000, 22050 y 44100 muestras por segundo)
- Profundidad de datos en bits (8 y 16 bits por conteo están disponibles)
- Número de canales (un canal para mono y dos para estéreo)
- Datos firmados o sin firmar que se utilizan en la secuencia (por ejemplo, el valor varía de 0 a 255 o de -127 a +127)
- El orden de bytes de Big-endian o little-endian. (si está transmitiendo una secuencia de bytes de valores de 16 bits, es importante saber qué byte viene primero, bajo o alto, ya que hay ambas opciones).
Como puede ver en el Listado 3, en nuestro caso, utilizamos los siguientes parámetros para una instancia del objeto AudioFormat.
- 8000 muestras por segundo
- 16 tamaño de datos
- datos significativos
- Orden little-endian
Por defecto, los datos están codificados por PCM lineal.
El constructor que utilizamos crea una instancia del objeto AudioFormat usando la modulación de código de pulso lineal y los parámetros indicados anteriormente (Volveremos a PCM lineal y otros métodos de codificación en las siguientes lecciones)
Volver al método playAudio nuevamenteAhora que entendemos cómo funciona el formato de datos de audio en el sonido Java, volvamos al método playAudio. Tan pronto como queramos reproducir los datos de audio disponibles, necesitamos un objeto de la clase AudioInputStream. Obtenemos una instancia de esto en el Listado 4.
audioInputStream = new AudioInputStream( byteArrayInputStream, audioFormat, audioData.length/audioFormat. getFrameSize());
Listado 4Parámetros para el constructor AudioInputStream- El constructor para la clase AudioInputStream requiere los siguientes tres parámetros:
- La secuencia en la que se basará la instancia del objeto AudioInputStream (como vemos para este propósito, usamos la instancia del objeto ByteArrayInputStream creado anteriormente)
- El formato de datos de audio para esta secuencia (para este fin ya hemos creado una instancia del objeto AudioFormat)
- El tamaño del marco (marco) para los datos en esta secuencia (consulte la descripción a continuación)
- Los primeros dos parámetros son claros del código en el Listado 4. Sin embargo, el tercer parámetro no es tan obvio en sí mismo.
Obtener tamaño de cuadroComo podemos ver en el Listado 4, el valor del tercer parámetro se crea usando cálculos. Este es solo uno de los atributos del formato de audio que no hemos mencionado antes, y se llama marco.
¿Qué es un marco?Para un PCM lineal simple utilizado en nuestro programa, el marco contiene un conjunto de muestras para todos los canales en un momento dado.
Por lo tanto, el tamaño de la trama es igual al tamaño del recuento en bytes multiplicado por el número de canales.
Como habrás adivinado, un método llamado getFrameSize devuelve el tamaño del cuadro en bytes.
Cálculo del tamaño del marcoPor lo tanto, la longitud de los datos de audio en un cuadro puede calcularse dividiendo el número total de bytes en la secuencia de datos de audio por el número de bytes en un cuadro. Este cálculo se usa para el tercer parámetro en el Listado 4.
Obteniendo un objeto SourceDataLineLa siguiente parte del programa que discutiremos es un sistema de salida de audio simple. Como podemos ver en el diagrama de la Fig. 5, para resolver este problema, necesitamos un objeto SourceDataLine.
Hay varias formas de obtener una instancia del objeto SourceDataLine, todas las cuales son muy complicadas. El código en el Listado 5 recupera y almacena una referencia a una instancia del objeto SourceDataLine.
(Tenga en cuenta que este código no solo crea una instancia del objeto SourceDataLine. Lo obtiene de una manera indirecta).
DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class, audioFormat); sourceDataLine = (SourceDataLine) AudioSystem.getLine( dataLineInfo);
Listado 5¿Qué es un objeto SourceDataLine?
Sobre esto, Sun escribe lo siguiente:
“SourceDataLine es un canal de datos en el que se pueden escribir datos. Funciona como una entrada para un mezclador. Una aplicación escribe una secuencia de bytes en una SourceDataLine, que almacena los datos y los entrega a su mezclador. El mezclador puede transmitir los datos que procesa para la siguiente etapa, por ejemplo, al puerto de salida.
Tenga en cuenta que la convención de nomenclatura para este emparejamiento refleja la relación entre el canal y su mezclador ".Método GetLine para la clase AudioSystemUna de las formas de obtener una instancia del objeto SourceDataLine es llamar al método getLine estático desde la clase AudioSystem (Tendremos mucho que informar sobre ello en las próximas lecciones).
El método getLine requiere un parámetro de entrada de tipo Line.Info y devuelve un objeto Line que coincide con la descripción en el objeto Line.Info ya definido.
Otra breve digresiónSun informa la siguiente información sobre el objeto Line.Info:
“El canal tiene su propio objeto de información (una instancia de Line.Info), que muestra qué mezclador (si lo hay) envía los datos de audio mezclados como salida directamente al canal, y qué mezclador (si lo hay) recibe los datos de audio como entrada directamente desde el canal. Las variedades de línea pueden corresponder a subclases de Line.Info, que le permite especificar otros tipos de parámetros relacionados con tipos específicos de canales "
DataLine.Info ObjectLa primera expresión en el Listado 5 crea una nueva instancia del objeto DataLine.Info, que es una forma especial (subclase) del objeto Line.Info.
Hay varios constructores sobrecargados para la clase DataLine.Info. Hemos elegido el más fácil de usar. Este constructor requiere dos parámetros.
Objeto de claseEl primer parámetro es Class, que representa la clase que definimos como SourceDataLine.class
El segundo parámetro determina el formato de datos deseado para el canal. Usamos una instancia del objeto AudioFormat para él, que ya se ha definido anteriormente.
Donde ya estamosDesafortunadamente, todavía no tenemos el objeto SourceDataLine más requerido. Hasta ahora, tenemos un objeto que solo proporciona información sobre el objeto SourceDataLine que necesitamos.
Obteniendo un objeto SourceDataLineLa segunda expresión en el Listado 5 finalmente crea y almacena la instancia de SourceDataLine que necesitamos. Esto sucede llamando al método estático getLine de la clase AudioSystem y pasando dataLineInfo como parámetro. (En la próxima lección, veremos cómo obtener el objeto Line, trabajando directamente con el objeto Mixer).
El método getLine devuelve una referencia a un objeto de tipo Line, que es el padre de SourceDataLine. Por lo tanto, se requiere un downcast aquí antes de que el valor de retorno se guarde como SourceDataLine.
Preparémonos para usar el objeto SourceDataLineUna vez que obtengamos una instancia del objeto SourceDataLine, debemos prepararlo para abrirlo y ejecutarlo, como se muestra en el Listado 6.
sourceDataLine.open(audioFormat); sourceDataLine.start();
Listado 6Método de aperturaComo puede ver en el Listado 6, enviamos el objeto AudioFormat al método de apertura para el objeto SourceDataLine.
Según Sun, este es un método:
"Abre una línea (canal) con un formato previamente definido, lo que le permite recibir los recursos del sistema que necesita y estar en condiciones de trabajo".Estado de descubrimientoHay poco más que Sun escribe sobre él en este hilo.
“Abrir y cerrar el canal afecta la distribución de los recursos del sistema. La apertura exitosa del canal asegura que todos los recursos necesarios se proporcionen al canal.
La apertura del mezclador, que tiene sus puertos de entrada y salida para datos de audio, incluye, entre otras cosas, el uso del hardware de la plataforma en la que se lleva a cabo el trabajo y la inicialización de los componentes de software necesarios.
Abrir un canal, que es una ruta para datos de audio hacia o desde un mezclador, incluye inicializarlo y recibir de ninguna manera recursos ilimitados del mezclador. En otras palabras, el mezclador tiene un número finito de canales, por lo que varias aplicaciones con sus propias necesidades de canal (e incluso a veces una aplicación) deben compartir correctamente los recursos del mezclador) "Llamar al método de inicio en un canalSegún Sun, llamar al método de inicio de un canal significa lo siguiente:
“El canal puede usar líneas de E / S. Si se intenta utilizar una línea ya operativa, el método no hace nada. Pero después de que el búfer de datos esté vacío, la línea reanuda el inicio de E / S, comenzando con el primer fotograma que no logró procesar después de que el búfer se haya cargado por completo ".En nuestro caso, por supuesto, el canal no se detuvo. Desde que lo lanzamos por primera vez.
Ahora tenemos casi todo lo que necesitamos.En este punto, hemos recibido todos los recursos de audio que necesitamos para reproducir los datos de audio que previamente hemos grabado y almacenado en una instancia del objeto ByteArrayOutputStream. (Recuerde que este objeto solo existe en la RAM de la computadora).
Comenzamos flujosCrearemos e iniciaremos la transmisión para reproducir el audio. El código en el Listado 7 crea e inicia este hilo.
(No confunda la llamada al método de inicio en este hilo con la llamada al método de inicio en el objeto SourceDataLine del Listado 6. Estas son operaciones completamente diferentes)
Thread playThread = new Thread(new PlayThread()); playThread.start(); } catch (Exception e) { System.out.println(e); System.exit(0); }
Listado 7Código sin pretensionesEl fragmento del programa en el Listado 7, aunque es muy simple, muestra un ejemplo de programación multiproceso en Java. Si no lo comprende, debe familiarizarse con este tema en temas especializados para aprender Java.
Una vez que se inicia la transmisión, funcionará hasta que todos los datos de audio pregrabados se hayan reproducido hasta el final.
Nuevo objeto de hiloEl código en el Listado 7 crea una instancia del objeto Thread de la clase PlayThread. Esta clase se define como una clase interna en nuestro programa. Su descripción comienza en el Listado 8.
class PlayThread extends Thread{ byte tempBuffer[] = new byte[10000];
Listado 8El método de ejecución en la clase ThreadExcepto por declarar una variable tempBuffer (que se refiere a una matriz de bytes), una definición completa de esta clase es solo una definición del método de ejecución. Como ya debería saber, llamar al método de inicio en un objeto Thread hace que se ejecute el método de ejecución de este objeto
El método de ejecución para este hilo comienza en el Listado 9.
public void run(){ try{ int cnt;
Listado 9La primera parte del fragmento de programa en el método de ejecución.El método de ejecución contiene dos partes importantes, la primera de las cuales se muestra en el Listado 9.
En resumen, aquí se usa un bucle para leer datos de audio de AudioInputStream y pasarlos a SourceDataLine.
Los datos enviados al objeto SourceDataLine se transfieren automáticamente a la salida de audio predeterminada. Puede ser un altavoz de computadora incorporado o una salida de línea. (Aprenderemos a determinar los dispositivos de sonido necesarios en las siguientes lecciones). La variable cnt y el búfer de datos tempBuffer se utilizan para controlar el flujo de datos entre las operaciones de lectura y escritura.
Lectura de datos de AudioInputStreamEl ciclo de lectura del objeto AudioInputStream lee el número máximo especificado de bytes de datos del AudioInputStream y coloca su matriz de bytes.
Valor de retornoAdemás, este método devuelve el número total de bytes leídos, o -1, si se alcanzó el final de la secuencia registrada. El número de bytes leídos se almacena en la variable cnt.
Bucle de escritura SourceDataLineSi el número de bytes leídos es mayor que cero, entonces hay una transición al ciclo de escritura de datos en SourceDataLine. En este bucle, los datos de audio ingresan al mezclador. Los bytes se leen de la matriz de bytes de acuerdo con sus índices y se escriben en el búfer del canal.
Cuando la secuencia de entrada se secaCuando el ciclo de lectura devuelve -1, esto significa que todos los datos de audio previamente grabados han finalizado y se pasa más control al fragmento de programa en el Listado 10.
sourceDataLine.drain(); sourceDataLine.close(); }catch (Exception e) { System.out.println(e); System.exit(0); }
Listado 10Bloquear y esperarEl código en el Listado 10 llama al método de drenaje en el objeto SourceDataLine para que el programa pueda bloquear y esperar a que el búfer interno se vacíe en SourceDataLine. Cuando el búfer está vacío, significa que toda la siguiente porción se entrega a la salida de sonido de la computadora.
Cerrando SourceDataLineLuego, el programa llama al método de cierre para cerrar el canal, lo que muestra que todos los recursos del sistema utilizados por el canal ahora son libres. Sun informa el siguiente cierre del canal:
“Cerrar el canal indica que todos los recursos involucrados para este canal pueden ser liberados. Para liberar recursos, la aplicación debe cerrar los canales, ya estén involucrados o no, así como cuándo finaliza la aplicación. Se supone que los mezcladores comparten recursos del sistema y pueden cerrarse y abrirse repetidamente. Otros canales pueden o no admitir la reapertura después de que se hayan cerrado. En general, los mecanismos para abrir líneas varían según los diferentes subtipos ".Y ahora el final de la historia.Así que aquí explicamos cómo nuestro programa usa la API de sonido Java para garantizar la entrega de datos de audio desde la memoria interna de la computadora a la tarjeta de sonido.
Ejecuta el programaAhora puede compilar y ejecutar el programa desde el Listado 11, que corona el final de nuestra lección.
Captura y reproduce datos de audioEl programa demuestra la capacidad de grabar datos desde un micrófono y reproducirlos a través de la tarjeta de sonido de su computadora. Las instrucciones para usarlo son muy simples.
Ejecute el programa La simple GUI GUI, que se muestra en la Figura 6, debería aparecer en la pantalla.

- Haga clic en el botón Capturar y grabe cualquier sonido en el micrófono.
- Haga clic en el botón Detener para detener la grabación.
- Haga clic en el botón Reproducción para reproducir la grabación a través de la salida de sonido de su computadora.
Si no escucha nada, intente aumentar la sensibilidad del micrófono o el volumen del altavoz.
El programa guarda un registro en la memoria de la computadora, así que tenga cuidado. Si intenta guardar demasiados datos de audio, puede quedarse sin RAM.
Conclusión- Descubrimos que la API de Java Sound se basa en el concepto de canales y mezcladores.
- Obtuvimos la información inicial sobre las características físicas y eléctricas del sonido analógico, para luego entender el dispositivo del mezclador de audio.
- Utilizamos un escenario de concierto de rock amateur con seis micrófonos y dos altavoces estéreo para describir la posibilidad de usar un mezclador de audio.
- Discutimos una serie de temas de programación de Java Sound, incluidos mezcladores, canales, formato de datos y más.
- Explicamos la relación general entre los objetos SourceDataLine, Clip, Mixer, AudioFormat y los puertos en un programa simple para generar datos de audio.
- Nos familiarizamos con un programa que nos permite grabar inicialmente y luego reproducir datos de audio.
- Recibimos una explicación detallada del código utilizado para reproducir datos de audio grabados previamente en la memoria de la computadora.
Que sigueEn este tutorial, descubrimos que la API de Java Sound se basa en el concepto de mezcladores y canales. Sin embargo, el código que discutimos no incluía mezcladores explícitamente. La clase AudioSystem nos proporcionó métodos estáticos que hacen posible escribir programas de procesamiento de audio sin acceder directamente a los mezcladores. En otras palabras, estos métodos estáticos nos quitan los mezcladores.
En la próxima lección, presentamos un código de captura de datos modificado en comparación con el presentado en esta lección. La nueva versión usará explícitamente mezcladores para mostrarle cómo usarlos cuando realmente los necesite. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import javax.sound.sampled.*; public class AudioCapture01 extends JFrame{ boolean stopCapture = false; ByteArrayOutputStream byteArrayOutputStream; AudioFormat audioFormat; TargetDataLine targetDataLine; AudioInputStream audioInputStream; SourceDataLine sourceDataLine; public static void main( String args[]){ new AudioCapture01(); }
Listado 11