2011-08-08 9 views
7

Quiero generar sonidos en función de la acción del usuario en Java. Incluso si configuré el tamaño del búfer en SourceDataLine al valor más pequeño posible (1 fotograma), aún tengo un retraso de aproximadamente 1 segundo.cómo transmitir sonido en java sin demora utilizando SourceDataLine

Debido a que un fragmento de código vale más que mil palabras (? O era una imagen), aquí está el código:

import javax.sound.sampled.AudioFormat; 
import javax.sound.sampled.AudioSystem; 
import javax.sound.sampled.DataLine; 
import javax.sound.sampled.SourceDataLine; 
import javax.swing.JFrame; 
import javax.swing.JSlider; 
import javax.swing.event.ChangeEvent; 
import javax.swing.event.ChangeListener; 

public class SoundTest { 

    private static int sliderValue = 500; 

    public static void main(String[] args) throws Exception { 
     final JFrame frame = new JFrame(); 
     final JSlider slider = new JSlider(500, 1000); 
     frame.add(slider); 
     slider.addChangeListener(new ChangeListener() { 
      @Override 
      public void stateChanged(ChangeEvent e) { 
       sliderValue = slider.getValue(); 
      } 
     }); 
     frame.pack(); 
     frame.setVisible(true); 

     final AudioFormat audioFormat = new AudioFormat(44100, 8, 1, true, true); 
     final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, 1); 
     final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info); 
     soundLine.open(audioFormat); 
     soundLine.start(); 
     byte counter = 0; 
     final byte[] buffer = new byte[1]; 
     byte sign = 1; 
     while (frame.isVisible()) { 
      if (counter > audioFormat.getFrameRate()/sliderValue) { 
       sign = (byte) -sign; 
       counter = 0; 
      } 
      buffer[0] = (byte) (sign * 30); 
      soundLine.write(buffer, 0, 1); 
      counter++; 
     } 
    } 
} 

trate de mover el cursor mientras se escucha el sonido. ¿Es posible, o debo crear búferes en memoria y envolverlos en instancias Clip?

Respuesta

14

La solución es especificar el tamaño del búfer en el método open(AudioFormat,int). Un retraso de 10ms-100ms será aceptable para audio en tiempo real. Las latencias muy bajas como no funcionarán en todos los sistemas informáticos, y 100 ms o más probablemente sean molestas para sus usuarios. Una buena compensación es, por ej. 50 ms. Para su formato de audio, 8 bits, mono a 44100Hz, un buen tamaño de búfer es 2200 bytes, que es casi 50 ms.

También tenga en cuenta que los diferentes sistemas operativos tienen capacidades de audio diferentes en Java. En Windows y Linux puede trabajar con tamaños de búfer bastante pequeños, pero OS X usa una implementación anterior con un retraso significativamente mayor.

Además, la escritura de bytes de datos por byte al SourceDataLine es muy ineficiente (el tamaño de búfer se establece en el método open(), no en write()), como una regla de oro que siempre iba a escribir un tamaño de búfer completo a la SourceDataLine .

Después de configurar la SourceDataLine, utilice este código:

final int bufferSize = 2200; // in Bytes 
soundLine.open(audioFormat, bufferSize); 
soundLine.start(); 
byte counter = 0; 
final byte[] buffer = new byte[bufferSize]; 
byte sign = 1; 
while (frame.isVisible()) { 
    int threshold = audioFormat.getFrameRate()/sliderValue; 
    for (int i = 0; i < bufferSize; i++) { 
     if (counter > threshold) { 
      sign = (byte) -sign; 
      counter = 0; 
     } 
     buffer[i] = (byte) (sign * 30); 
     counter++; 
    } 
    // the next call is blocking until the entire buffer is 
    // sent to the SourceDataLine 
    soundLine.write(buffer, 0, bufferSize); 
} 
+0

Gracias. Estaba cegado por el argumento _bufferSize_ en _new DataLine.Info (SourceDataLine.class, audioFormat, 1) _. Por supuesto que no voy a usar un buffer tan pequeño. Esto fue solo para mostrar mi problema. – andi

+0

@Florian gracias por este ejemplo. ¿Y qué significa si 'int n = soundLine.write (buffer, 0, bufferSize);' n devuelve 0 valor después de 2 primera escritura? – user390525

+0

@ user390525, según la especificación de SourceDataLine.write(), solo puede devolver menos del tamaño de búfer especificado en caso de error (parámetros mal formados) o si SourceDataLine se detuvo, se vació o se cerró. Si está 100% seguro de que ninguna de esas condiciones se aplica, puede haber un error en la implementación de Java de esa SourceDataLine. – Florian

Cuestiones relacionadas