用Java播放游戏音乐 – 如何阻止数据流?

我有这个音乐课,我正在高中的入门课程中制作一个游戏。 您可以看到我从中获取代码的源代码。

我最初使用的是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; } } 

我不确定你接近可怕的声音,但我想我已经知道如何解决你提到的重叠问题。 看起来你对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"); 

乍一看,这看起来很好,毕竟你在开始歌曲B之前小心地停止了歌曲,对吧? 不幸的是,当谈到并发时,很少有事情像我们希望的那样简单。 让我们看看你的一小部分代码,特别是负责将数据输入音频流的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分钟内脱离了我的头顶。 我推荐关于线程的The Java Tutorials系列 。