Проблема с Javas Audio Clips при частом воспроизведении звуковых сигналов - PullRequest
2 голосов
/ 08 июля 2011

Я хочу воспроизводить короткие звуковые сигналы (файлы WAV) при успешном завершении и ошибке действия, вызванного графическим интерфейсом.

Я наткнулся на javax.sound.sampled.Clip , который, казалось, работал. Вот основной код, который я использую:

clip.stop();
clip.setFramePosition(0);
clip.start();

Это выполняется после нажатия кнопки запускает действие базы данных. В случае успеха и ошибки воспроизводятся два разных предварительно загруженных клипа.

Но на производственном компьютере (старый ПК с Kubuntu 10.4) через некоторое время (около 400+ выполнений или 2-4 часа) клип отказывается воспроизводиться. Метод остановки занимает около 3 секунд, и следующее действие запуска не воспроизводит звук. Каждый последующий вызов кода завершается ошибкой, не вызывая исключения или каких-либо других отзывов. Единственное, что исправляет это, это перезапуск всего приложения.

Мои вопросы: Есть ли обходной путь для этого? У кого-нибудь еще есть такая же проблема? Или есть другой фреймворк, который я могу использовать для воспроизведения как минимум двух разных звуков (Toolkit.beep () может воспроизводить только один звук).

Ответы [ 2 ]

2 голосов
/ 09 июля 2011

Не бойтесь просто воссоздать объекты, накладные расходы низкие. Вместо сброса клипов попробуйте просто создать новые. Вы можете кэшировать файлы, это будет полезной оптимизацией. Повторное использование объектов клипа не.

Или вы можете попробовать альтернативную реализацию [которая не ограничена] .

Это лучший результат в Google для 'java play wav files':

http://www.anyexample.com/programming/java/java_play_wav_sound_file.xml

Это упрощает все до одного вызова:

new AePlayWave("test.wav").start();

Просто добавьте этот класс в свою кодовую базу:

import java.io.File; 
import java.io.IOException; 
import javax.sound.sampled.AudioFormat; 
import javax.sound.sampled.AudioInputStream; 
import javax.sound.sampled.AudioSystem; 
import javax.sound.sampled.DataLine; 
import javax.sound.sampled.FloatControl; 
import javax.sound.sampled.LineUnavailableException; 
import javax.sound.sampled.SourceDataLine; 
import javax.sound.sampled.UnsupportedAudioFileException; 

public class AePlayWave extends Thread { 

    private String filename;

    private Position curPosition;

    private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb 

    enum Position { 
        LEFT, RIGHT, NORMAL
    };

    public AePlayWave(String wavfile) { 
        filename = wavfile;
        curPosition = Position.NORMAL;
    } 

    public AePlayWave(String wavfile, Position p) { 
        filename = wavfile;
        curPosition = p;
    } 

    public void run() { 

        File soundFile = new File(filename);
        if (!soundFile.exists()) { 
            System.err.println("Wave file not found: " + filename);
            return;
        } 

        AudioInputStream audioInputStream = null;
        try { 
            audioInputStream = AudioSystem.getAudioInputStream(soundFile);
        } catch (UnsupportedAudioFileException e1) { 
            e1.printStackTrace();
            return;
        } catch (IOException e1) { 
            e1.printStackTrace();
            return;
        } 

        AudioFormat format = audioInputStream.getFormat();
        SourceDataLine auline = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

        try { 
            auline = (SourceDataLine) AudioSystem.getLine(info);
            auline.open(format);
        } catch (LineUnavailableException e) { 
            e.printStackTrace();
            return;
        } catch (Exception e) { 
            e.printStackTrace();
            return;
        } 

        if (auline.isControlSupported(FloatControl.Type.PAN)) { 
            FloatControl pan = (FloatControl) auline
                    .getControl(FloatControl.Type.PAN);
            if (curPosition == Position.RIGHT) 
                pan.setValue(1.0f);
            else if (curPosition == Position.LEFT) 
                pan.setValue(-1.0f);
        } 

        auline.start();
        int nBytesRead = 0;
        byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];

        try { 
            while (nBytesRead != -1) { 
                nBytesRead = audioInputStream.read(abData, 0, abData.length);
                if (nBytesRead >= 0) 
                    auline.write(abData, 0, nBytesRead);
            } 
        } catch (IOException e) { 
            e.printStackTrace();
            return;
        } finally { 
            auline.drain();
            auline.close();
        } 

    } 
} 
1 голос
/ 12 июля 2011

Так как я это исправил:

Я в основном следовал совету Чарльза Гудвина, но я изменил класс AePlayWave, чтобы реализовать runnable и использовать его с пулом потоков (чтобы избежать накладных расходов при запуске новых потоков все время). Кроме того, я использую URL-адреса, а не файлы, чтобы использовать ресурсы из упакованного файла JAR. Объект AePlayWave создается, когда настройка завершена (файлы выбраны) или настройки изменены. Существует экземпляр для каждого звука, который я хочу, чтобы приложение воспроизводило. Затем методы прослушивателя событий запускают пул для запуска конкретного экземпляра AePlayWave для этого звука событий. Остальное в основном то же самое.

Есть только две неудобные проблемы:

1.) На слабых машинах окончание WAV не всегда воспроизводится. Когда звуки очень короткие (например, звуковой сигнал 100 мс), это может привести к тому, что звук не будет воспроизводиться вообще! Вот почему я добавил 500 мс тишины в конец каждого звука, который я хотел бы воспроизвести. Это обходной путь, но он помогает, и на данный момент он кажется лучшим и наиболее стабильным подходом.

2.) Если воспроизводится более одного звука (из-за очень быстрого повторения), звуки перекрываются, и вы слышите изменение мелодии и громкости. Это нормально в моем случае, но может быть раздражающим для других целей.

Он уже работает в продуктивной системе. Если мне сообщат о каких-либо ошибках, я отредактирую это сообщение, чтобы вы были в курсе.

Теперь вот (в основном уменьшенный) исходный код:

import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;    
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

public class AudibleListener implements SomeListener {

private Runnable successRunner;
private Runnable failRunner;

ExecutorService pool = Executors.newCachedThreadPool();

/**
 * Call this after initialization and after every change in your config at runtime.
 */
public void reloadSettings() {
    // put your configuration here
    this.successRunner = new WavePlayer(this.getClass().getResource("success.wav"));
    this.failRunner = new WavePlayer(this.getClass().getResource("fail.wav"));
}

/**
 * Call this to savely shutdown the thread pool.
 */
public void shutdown() {
    this.pool.shutdown();
}

/**
 * Listener method called on success. 
 */
public void eventSuccess() {
    this.pool.execute(this.successRunner);
}

/**
 * Listener method called on fail. 
 */
public void eventFailed() {
    this.pool.execute(this.failRunner);
}

private class WavePlayer implements Runnable {

    private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb 

    private URL soundFile;

    public WavePlayer(URL soundFile) {
        this.soundFile = soundFile;
    }

    @Override
    public void run() {

        try {
            // check if the URL is still accessible!
            this.soundFile.openConnection().connect();
            this.soundFile.openStream().close();
        } catch (Exception e) {
            return;
        }

        AudioInputStream audioInputStream = null;
        try {
            audioInputStream = AudioSystem
                    .getAudioInputStream(this.soundFile);
        } catch (UnsupportedAudioFileException e) {
            return;
        } catch (IOException e) {
            return;
        }

        AudioFormat format = audioInputStream.getFormat();
        SourceDataLine auline = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

        try {
            auline = (SourceDataLine) AudioSystem.getLine(info);
            auline.open(format);
        } catch (LineUnavailableException e) {
            return;
        } catch (Exception e) {
            return;
        }

        auline.start();
        int nBytesRead = 0;
        byte[] abData = new byte[this.EXTERNAL_BUFFER_SIZE];

        try {
            while (nBytesRead != -1) {
                nBytesRead = audioInputStream
                        .read(abData, 0, abData.length);
                if (nBytesRead >= 0) {
                    auline.write(abData, 0, nBytesRead);
                }
            }
        } catch (IOException e) {
            return;
        } finally {
            auline.drain();
            auline.close();
        }
    }
}
}

Приветствия и спасибо за помощь!

P.

Обновление:

Это работает в течение последних 72 часов без ошибок! Похоже, мы сделали это!

...