Воспроизведение аудио из моего Loop один раз без остановки остальной части цикла

Скажем, у меня есть основной GameLoop

Внутри этого цикла у меня есть обновления игры

Я обрабатываю свои события для тестирования столкновений спрайтов с каждой итерацией

Если столкновение верно, воспроизвести аудиофайл

Вот где возникает проблема

Аудиоклип будет воспроизводиться быстро, пока игра зависла.

or

Он будет воспроизводиться с задержкой, как я хочу, но вся игра останавливается, кроме аудиоклипа.

Мне просто нужны советы по многопоточности. Насколько мне известно, это будет лучший способ справиться с этой проблемой, и я не могу заставить его работать правильно.

Примечание. Я бы расширил Thread в основном классе, но уже расширяет Canvas, что необходимо.

    public Main()
    {

        boolean running = true;

        while(running)
        {

            // check for collision (returns boolean)
            // if true proceed to execute Entity.doLogic()
            // this then activates the AudioClip class' .playAudioClip(this, path)
            // the audio Clip is then played and once it's done it'll return
            // returns and instantly goes back to playing again
            // meanwhile the loop Freezes up on me.

        }

    }

И это настоящий Sound.class

public class Sounds
{

    public void startSound()
    {
        String path = "path";
        playAudioClip(game, path);
    }

    public void playAudioClip(String path)
    {
        try
        {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(new File(path)));
            clip.start();
        }
        catch(Exception e)
        {
            System.out.println("Problem loading audio file");
        }
        try{Thread.sleep(500);}catch(Exception ex){System.out.println("Problem with Sleep");};
    }
}

Я пробовал приведенную ниже и ту же ситуацию (вызов ее с помощью s.start() и s.run() без разницы), использование .start() вызовет ошибку в потоке, быстро воссоздает и поделится)

public class Sounds extends Thread
{
    @Override
    public void run()
    {
        String path = "path";
        playAudioClip(game, path);
    }

    public void playAudioClip(String path)
    {
        try
        {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(new File(path)));
            clip.start();
        }
        catch(Exception e)
        {
            System.out.println("Problem loading audio file");
        }
        try{Thread.sleep(500);}catch(Exception ex){System.out.println("Problem with Sleep");};
    }
}

выдает на консоль «java.lang.IllegalThreadStateException», вызывая с помощью start() только определение run() внутри этого объекта

введите здесь описание изображения Примечание для себя. Не делай этого.

Неправильная многопоточность. Яркий пример: 243 максимальных потока, идущих в любой момент.


person Kuroi Akuma    schedule 06.04.2020    source источник


Ответы (1)


Когда выполняется метод start() для Clip, звук воспроизводится через поток демона, созданный специально для этой цели. Вам не нужно создавать Thread и включать команду Thread.sleep(). Пока ваша программа работает, звук будет выполняться. Если ваша программа остановится во время воспроизведения звука, потому что она находится на демоне, звук не остановится, а остановится вместе с программой.

Я предполагаю, что ваш код взят из примера программы, единственной задачей которой является воспроизведение звука. Лучший демонстрационный пример показал бы, как управлять Clip в контексте более крупной программы.

Вот класс, чтобы попробовать. Я пропустил обработку Exception. Я использую URL, потому что это позволяет получить ресурсы, даже если программа была скомпилирована в банку. Что касается файловой структуры, код предполагает, что звуковые файлы находятся во вложенной папке относительно расположения вашего класса Sounds.

public class Sounds
{
    private Clip clip;

    public Sounds(String soundFileName)
    {
        URL url = this.class.getResource("audio/" + soundFileName);
        AudioInputStream ais = AudioSystem.getAudioInputStream(url);
        DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
        clip = (Clip) AudioSystem.getLine(info);
        clip.open(ais);
    }

    public void playAudioClip()
    {
        clip.setFramePosition(0);
        clip.start();
    } 
}

Класс Clip был разработан для звуков, хранящихся в памяти. Если вы не хотите держать звуковой файл в памяти, вам следует использовать SourceDataLine, так как он будет запускаться быстрее и одновременно будет хранить в памяти только объем данных буфера, а не весь файл.

Таким образом, мы делаем Clip переменной экземпляра, загружаем и открываем ее как часть процесса инстанцирования.

Команда clip.setFramePosition(0) предназначена для того, чтобы каждый раз, когда вы вызываете Clip, он начинал воспроизведение с начала файла. При этом вы можете звонить и играть в Clip в любое время. В худшем случае, если он находится в середине воспроизведения, когда вы его вызываете, звук вернется к началу и начнется заново.

person Phil Freihofner    schedule 22.08.2020