2011-08-08 8 views
7

Chcę generować dźwięki w oparciu o działania użytkownika w Javie. Nawet jeśli ustawię rozmiar bufora w SourceDataLine na najmniejszą możliwą wartość (1 klatkę) nadal mam opóźnienie około 1 sekundy.jak przesyłać dźwięk w Javie bez opóźnienia przy użyciu SourceDataLine

Ponieważ fragment kodu jest wart tysiąca słów (? Czy był to obraz), oto kod:

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

Spróbuj przesunąć suwak podczas słuchania dźwięku. Czy to możliwe, czy też muszę tworzyć bufory w pamięci i owijać je w instancje Clipa?

Odpowiedz

14

Poprawka polega na określeniu rozmiaru bufora w metodzie open(AudioFormat,int). Opóźnienie w zakresie 10ms-100ms będzie możliwe do zaakceptowania w przypadku dźwięku w czasie rzeczywistym. Bardzo niskie opóźnienia nie będą działać na wszystkich systemach komputerowych, a 100 ms lub więcej prawdopodobnie będzie denerwujące dla użytkowników. Dobrym kompromisem jest np. 50ms. W przypadku formatu audio, 8-bitowego, mono o częstotliwości 44100 Hz, dobry rozmiar bufora to 2200 bajtów, czyli prawie 50 ms.

Należy również pamiętać, że różne systemy operacyjne mają różne możliwości audio w Javie. W systemach Windows i Linux można pracować z dość małymi rozmiarami buforów, ale system OS X używa starej implementacji ze znacznie większym opóźnieniem.

Również pisanie bajt danych przez bajt do SourceDataLine jest bardzo nieefektywne (rozmiar bufora jest ustawiona w metodzie open(), a nie w write()), jako zasada bym zawsze pisać jeden pełny rozmiar bufora do SourceDataLine .

Po skonfigurowaniu SourceDataLine, użyj tego kodu:

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

Dzięki. Zostałam zaślepiona argumentem _bufferSize_ w _new DataLine.Info (SourceDataLine.class, audioFormat, 1) _. Oczywiście nie zamierzam używać tak małego bufora. To tylko po to, by pokazać mój problem. – andi

+0

@Florian dziękuję za ten przykład. A co oznacza, jeśli 'int n = soundLine.write (buffer, 0, bufferSize); n zwraca wartość 0 po 2 pierwszym zapisie? – user390525

+0

@ user390525, zgodnie ze specyfikacją SourceDataLine.write(), może zwrócić tylko mniej niż określony rozmiar bufora w przypadku błędu (nieprawidłowe parametry) lub jeśli SourceDataLine został zatrzymany, przepłukany lub zamknięty. Jeśli masz 100% pewności, że żaden z tych warunków nie ma zastosowania, być może wystąpił błąd w implementacji Java tej SourceDataLine. – Florian