2011-06-06 64 views
2

我在高中的简介课程中为这款游戏设计了这个音乐课。你可以看到the source我从哪里得到的代码。在Java中玩游戏音乐 - 如何停止数据流?

我最初使用的是Clip,但发现它的缓冲区尺寸非常小,无法播放很长的歌曲(至少在学校我们的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; 
} 


} 
+0

1)我听说Java Sound在Mac上有问题。 2)'playMusic(String loc)'我不寒而栗地看到代表'File'对象的'String'参数。如果它是需要的“文件”,则不要接受其他文件。 3)虽然在这个问题上,在大多数情况下,** InputStream **作为参数击败了一个'File'。一个'InputStream'可以来自File或URL(可以包含档案中的资源)或ByteArrayInputStream'构造在内存中。 – 2011-06-07 06:18:05

+0

1.是的。总体而言,Clip并不是那么好,并且对于可以提供多少数据的限制有很小的限制,因为它将它完全存储在内存中。我在PC上没有问题,但在Mac上,我遇到了与尺寸有关的问题。 2.对不起,我仍然是一个初学者,17没有真正的正式教育(高中入门课很难算) - 我不得不自学的大部分事情,我会记住这一点。 3。诚然,尽管我不得不重新编写一个项目,但我确实尽可能地提高效率,这绝对是一种更好的方式,尽管对于游戏来说有些不必要。 – CMahaff 2011-06-08 19:56:14

回答

5

我不知道你越来越接近上可怕的声音,但我想我有一个如何解决你提到的重叠问题的想法。它看起来像你的声音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开始之前停止SONGA,对不对?不幸的是,当涉及到并发时,很少有事情像我们想要的那样简单。让我们来看看你的代码的一个小一点,特别是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是相当数量的音频数据,并且取决于音频属性会持续一段时间。你真正想要做的是通知线程退出(即设置为假),,然后等待线程完成。我相信这可以解决重叠问题,如果你没有正确地处理你的线索,这个问题可能也会导致戒烟的恐惧。我谦恭地提出了以下更改您的音乐类(不保证正确性)...

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' series on threading

+0

感谢您的回答!该项目今天到期(必须在昨天完成),所以我没有得到不幸的尝试你的解决方案。通过冲洗soundline(和其他一些东西),我能够让音频停止。你说得很对,为什么它重叠,声音在下一次开始播放之前就没有停止过。关闭时的可怕声音*我认为*是由于音频没有在退出时播放完成的。如果有更好的退出方式,并且结束时等待200毫秒,那么看起来没问题,除非点击左上角的X按钮并强制关闭。 – CMahaff 2011-06-08 19:54:16