2011-06-06 63 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