Метод слива в классе Java Clip

Я создаю короткий клип, а затем воспроизвожу его, вызывая метод start(). Затем я вызываю метод drain(), чтобы заблокировать выполнение до тех пор, пока воспроизведение клипа не будет завершено. Однако при запуске в несколько раз кода ниже, иногда это работает, иногда это не так, и звук останавливается случайным образом до конца.

Mixer mixer = AudioSystem.getMixer(null);
AudioFormat format = new AudioFormat(44100, 8, 1, true, false);
DataLine.Info info = new DataLine.Info(Clip.class, format);
try {
    // Create a sound of 1 second
    Clip clip = (Clip)mixer.getLine(info);
    byte[] b = new byte[44100];
    for(int i=0; i<b.length; i++)
        b[i] = (byte) (50*Math.sin(i/10.0));

    clip.open(format, b, 0, b.length);
    clip.setFramePosition(0);
    clip.start();
    clip.drain();
} catch(LineUnavailableException lue) { lue.printStackTrace(); }

Итак, мой вопрос: это ошибка? или я не понял метод drain()?

Ответ 1

Кажется, что clip.start() иногда возвращается непосредственно перед запуском события с ошибкой (?), и в этом случае дренаж не влияет. По обходному пути мы можем использовать Listener, чтобы поймать событие START:

Обходное решение:

Mixer mixer = AudioSystem.getMixer(null);
AudioFormat format = new AudioFormat(44100, 8, 1, true, false);
DataLine.Info info = new DataLine.Info(Clip.class, format);
try {
    // Create a sound of 1 second
    Clip clip = (Clip)mixer.getLine(info);

    //Workaround part 1
    clip.addLineListener(new LineListener() {
        @Override
        public void update(LineEvent event) {
            if (event.getType() == Type.START)
                synchronized (clip) {
                    clip.notify();
                }
        }
    });

    byte[] b = new byte[44100];
    for(int i=0; i<b.length; i++)
        b[i] = (byte) (50*Math.sin(i/10.0));

    clip.open(format, b, 0, b.length);
    clip.setFramePosition(0);

    //Workaround part 2
    synchronized (clip) {
        clip.start();
        clip.wait();
        System.out.println("Started");
    }
    clip.drain();
    System.out.println("Drained");
} catch(LineUnavailableException | InterruptedException lue) { lue.printStackTrace(); }