Воспроизведение игровой музыки на Java - Как остановить поток данных? - PullRequest
2 голосов
/ 07 июня 2011

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

Изначально я использовал Clip, но обнаружил, что у него довольно маленький размер буфера и он не может хорошо проигрывать длинные песни (по крайней мере, на наших компьютерах Mac в школе).Новый код работает хорошо, насколько я понимаю, он получает «куски» песни, проигрывает их, затем получает больше кусков.Проблема в том, что когда музыка меняется, песни иногда пересекаются, и когда я выхожу из игры, звучит ужасный звук (по крайней мере, на Mac), вероятно, из-за того, что поток данных обрезается до закрытия игры.

Можно ли как-то это исправить, кроме задержки программы на несколько секунд каждый раз?(Примечание: я не хочу использовать внешние .jar или библиотеки, я бы хотел, чтобы они строго соответствовали JRE)

package mahaffeyfinalproject;

import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;


public class Music implements Runnable{

//SOURCE: http://www.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html
//Note: I've modified it slightly

private String location;
private boolean play;

public void Music(){

}

public void playMusic(String loc) {
    location = loc;
    play = true;
    try{
        Thread t = new Thread(this);
        t.start();
    }catch(Exception e){
        System.err.println("Could not start music thread");
    }
}

public void run(){
    SourceDataLine soundLine = null;
    int BUFFER_SIZE = 64*1024;

    try {
        File soundFile = new File(location);
        AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
        AudioFormat audioFormat = audioInputStream.getFormat();
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        soundLine = (SourceDataLine) AudioSystem.getLine(info);
        soundLine.open(audioFormat);
        soundLine.start();
        int nBytesRead = 0;
        byte[] sampledData = new byte[BUFFER_SIZE];

        while (nBytesRead != -1 && play == true) {
            nBytesRead = audioInputStream.read(sampledData, 0, sampledData.length);
            if (nBytesRead >= 0) {
               soundLine.write(sampledData, 0, nBytesRead);
            }
        }
    } catch (Exception e) {
        System.err.println("Could not start music!");
    }

    soundLine.drain();
    soundLine.close();

}

public void stop(){
    play = false;
}


}

1 Ответ

5 голосов
/ 07 июня 2011

Я не уверен насчет ужасного звука, который вы слышите близко, но я думаю, что у меня есть идея, как решить проблему перекрытия, о которой вы упомянули. Похоже, что вы используете Sound API нормально, но я подозреваю, что у вас может быть проблема с параллелизмом. Я заметил, что вы начинаете новую тему для каждого звука, который вы играете. Это хорошая идея, но вы должны быть осторожны, если не хотите, чтобы ваши песни смешивались. Моя теория заключается в том, что ваш код выглядит примерно так:

Music songA = new Music();
Music songB = new Music();

//Start playing song A
songA.playMusic("path");

//Some stuff happens with your game...

//Some time later you want to change to song B
songA.stop();
songB.playMusic("other path");

На первый взгляд все выглядит хорошо, в конце концов, вы были осторожны, чтобы остановить songA перед запуском songB, верно? К сожалению, когда дело доходит до параллелизма, некоторые вещи настолько просты, насколько нам этого хотелось бы. Давайте рассмотрим небольшой кусочек вашего кода, в частности цикл while, отвечающий за подачу данных в аудиопоток ...

//Inside run(), this is the interesting bit at the end

while(nBytesRead != -1 && play == true){
    //Feed the audio stream while there is still data 
}

stop() устанавливает play в значение false, вызывая выход из цикла while, после завершения текущей итерации . Если я не ошибаюсь, каждая итерация вашего цикла while отправляет 64k ​​данных в аудиопоток. 64k - это достаточное количество аудиоданных, и в зависимости от свойств звука будет продолжаться в течение некоторого времени. То, что вы действительно хотите сделать, это сообщить потоку о выходе (то есть установить play в false), и затем ждать завершения потока . Я полагаю, что это решит проблему перекрытия, и если вы не убиваете свои потоки должным образом, эта проблема может также вызвать ужасную ракетку при выходе. Я смиренно предлагаю следующее изменение в вашем музыкальном классе (без гарантии правильности) ...

public class Music implements Runnable{
    private Thread t;    //Make the thread object a class field

    //Just about everything is the same as it was before...

    public void stop(){
        play = false;
        try{
            t.join()
        }catch(InterruptedException e){
            //Don't do anything, it doesn't matter
        }
   }
}

Надеюсь, это поможет, и ради любви ко всему хорошему и чистому, не используйте мой код как есть, он сошел с моей головы примерно через 2 минуты. Я рекомендую Серия Java Tutorials по многопоточности .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...