2011-08-08 2 views
7

Java에서 사용자 작업에 따라 소리를 생성하려고합니다. SourceDataLine의 버퍼 크기를 가능한 한 작은 값 (1 프레임)으로 설정하더라도 약 1 초의 지연이 있습니다. 소리를 들으면서SourceDataLine을 사용하여 java에서 지연없이 사운드를 스트리밍하는 방법

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++; 
     } 
    } 
} 

이 슬라이더를 움직여보십시오 : 코드 조각은 (? 또는 사진이었다) 천 개 단어의 가치가 있기 때문에

, 여기에 코드입니다. 가능한가, 아니면 메모리 내 버퍼를 만들어 Clip 인스턴스에 래핑해야합니까?

답변

14

해결 방법은 open(AudioFormat,int) 방법으로 버퍼 크기를 지정하는 것입니다. 실시간 오디오에는 10ms-100ms의 지연 시간이 허용됩니다. 매우 낮은 대기 시간은 모든 컴퓨터 시스템에서 작동하지 않으며 100ms 이상은 사용자에게 짜증을냅니다. 좋은 절충점은 예를 들어 50ms. 44100Hz에서 오디오 형식 인 8 비트, 모노의 경우 양호한 버퍼 크기는 2200 바이트이며 거의 50ms입니다.

다른 OS에는 Java의 오디오 기능이 다릅니다. Windows와 Linux에서는 매우 작은 버퍼 크기로 작업 할 수 있지만 OS X는 지연이 현저하게 오래 된 구현을 사용합니다.

또한,은 SourceDataLine에 바이트 데이터 바이트를 기록하는 것은 I 항상은 SourceDataLine 한 전체 버퍼 크기를 써서 경험적으로 (버퍼 크기가되지 write()에서 open() 방법에 설정된다) 아주 비효율적 .

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

감사 :

은 SourceDataLine을 설정 한 후,이 코드를 사용합니다. _new DataLine.Info (SourceDataLine.class, audioFormat, 1) _의 _bufferSize_ 인수로 눈이 멀었습니다. 물론 작은 버퍼는 사용하지 않을 것입니다. 이것은 단지 내 문제를 보여주는 것이 었습니다. – andi

+0

@Florian이 예를 들어 주셔서 감사합니다. 그리고 만약 int n = soundLine.write (buffer, 0, bufferSize);'n은 2 번째로 쓰여진 후에 0 값을 반환합니까? – user390525

+0

@ user390525, SourceDataLine.write() 스펙에 따라 오류 (잘못된 매개 변수)가 발생하거나 SourceDataLine이 중지, 플러시 또는 닫기 된 경우에만 지정된 버퍼 크기보다 작게 반환 할 수 있습니다. 이러한 조건 중 어느 것도 적용되지 않는다고 확신하는 경우 해당 SourceDataLine의 Java 구현에 버그가있을 수 있습니다. – Florian