comment diffuser du son en java sans délai en utilisant SourceDataLine


Je veux générer des sons basés sur l'action de l'utilisateur en Java. Même si je mets la taille du tampon dans SourceDataLine à la plus petite valeur possible (1 image), j'ai toujours un retard d'environ 1 seconde.

Parce qu'un extrait de code vaut mille mots (ou était-ce une image?), voici le code:

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

Essayez de déplacer le curseur tout en écoutant le son. Est-ce possible, ou dois-je créer des tampons en mémoire et les envelopper dans des instances de clip?

Author: andi, 2011-08-08

1 answers

Le correctif consiste à spécifier la taille du tampon dans la méthode open(AudioFormat,int). Un délai de 10 ms-100 ms sera acceptable pour l'audio en temps réel. Les latences très faibles comme ne fonctionneront pas sur tous les systèmes informatiques, et 100 ms ou plus seront probablement ennuyeux pour vos utilisateurs. Un bon compromis est, par exemple 50ms. Pour votre format audio, 8 bits, mono à 44100Hz, une bonne taille de tampon est de 2200 octets, ce qui est presque 50ms.

Notez également que différents OS ont des capacités audio différentes en Java. Sous Windows et Linux vous pouvez travailler avec des tailles de tampon assez petites, mais OS X utilise une ancienne implémentation avec un délai beaucoup plus important.

De plus, écrire des données octet par octet dans la SourceDataLine est très inefficace (la taille du tampon est définie dans la méthode open(), pas dans write()), en règle générale, j'écrirais toujours une taille de tampon complète dans la SourceDataLine.

Après avoir configuré la SourceDataLine, utilisez ce code:

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);
}
 15
Author: Florian, 2011-08-09 09:35:17