2011-08-08 15 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++; 
     } 
    } 
} 

は、音を聞きながら、スライダーを移動してみてください。それは可能ですか、またはインメモリバッファを作成してクリップインスタンスにラップする必要がありますか?

答えて

14

open(AudioFormat,int)メソッドでバッファサイズを指定することです。リアルタイムオーディオには10ms〜100msの遅延が許容されます。すべてのコンピュータシステムで動作しないような非常に低い待ち時間は、おそらく100ミリ秒以上はあなたのユーザーのために迷惑になるでしょう。良好なトレードオフは、例えば次のようなものである。 50ms。あなたのオーディオフォーマットでは、44100Hzの8ビット、モノラル、良いバッファサイズは約50msである2200バイトです。

Javaでは、さまざまなOSのオーディオ機能が異なります。 WindowsとLinuxでは、非常に小さなバッファサイズで作業することができますが、OS Xは遅延がかなり大きい古い実装を使用します。

また、SourceDataLineのにデータを1バイトごとに書き込みが(バッファサイズがないwrite()で、open()方法に設定されている)非常に非効率的で、経験則として、私はいつもはSourceDataLineに1つのフルバッファサイズを記述します。 。

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が停止、フラッシュ、またはクローズした場合に、指定されたバッファサイズよりも小さい値を返すことができます。 これらの条件がどれも適用されないことを100%確信している場合は、そのSourceDataLineのJava実装にバグが存在する可能性があります。 – Florian

関連する問題