2014-05-16 46 views
0

我想在背景中播放聲音文件(.wav),並有一個振動塊。 所以我已經編寫了一個JFrame,裏面有一個面板和一個畫布,其中我有一個可以通過鍵盤的箭頭控制的塊。我的主要方法有一個循環,作品粗暴地說,像這樣:在背景中播放聲音文件(.wav),並有一個振動塊

while (play) { 
     Date delay_time = new Date(); 
     delay_time.setTime(delay_time.getTime() + (int) (1000*(1.0/FPS))); 
     Graphics g = can.getGraphics(); 
     can.update(g); 

     //Here is my other stuff, like the Motion of the Block etc... 


     while(new Date().before(delay_time)) { 

     } 

    } 

我的FPS變量是當前設置爲30

一個靜態的最終詮釋現在我想實現一個背景音樂是不斷演奏。這將是一些eletrical/dubstep的東西。

現在我的問題是: 我該如何實現該音樂和(最重要的!)如何根據音樂使塊震動。 (如果你不明白我對音樂的振動意味着什麼,也許你可以通過解釋我如何獲得音量,共鳴或類似的東西來幫助我,在音樂的播放時間期間應該會不斷變化。)

由於

+0

這是一個相當寬泛的問題,我想打破它分爲兩個更集中的問題,比如「如何你是否在Java Swing應用程序中播放音樂?「 (我認爲人們會把你引導到JMF)和「你如何爲可視化樣本播放一段音樂?」 (我想你會被告知快速傅里葉變換(FFT))。 –

+0

當然,當你打字的時候,這個人很可能會丟掉你正在看的完美的例子。這就是有時這樣工作的方式:) –

回答

2

幀是樣品在所有通道中的音頻文件的橫截面。因此,一個16位立體聲(雙聲道)音頻文件將具有32位幀(每個採樣16位*每幀2個通道=每幀32位)。

裝入原始數據

爪哇8位字節讀出原始音頻數據,但大多數的音頻具有更高的樣本大小。因此,爲了表示音頻,您必須合併多個字節才能以音頻格式創建樣本。但首先,在將字節組合到採樣中之前,您需要將所有音頻加載到緩衝區中。

開始從文件中獲取音頻流:

File file = new File(filename); 
    AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file); 

現在,你有的AudioInputStream,您可以在音頻數據讀出。 AudioInputStream有一個read()方法,它接受一個未填充的字節[],並讀取數據中byte []的長度。要一次性讀取整個音頻文件,請在整個音頻文件的長度上創建一個字節[]。以字節爲單位的文件的完整長度爲:

的每幀的字節=字節總數*幀

總數

你可以得到的幀的整個文件(幀長度)的數量和的大小幀(幀大小)從AudioInputStream:

int frameLength =(int)audioInputStream.getFrameLength(); int frameSize =(int)audioInputStream.getFormat()。getFrameSize();

您可以創建一個長度設定爲幀長度*框架尺寸字節[]:

byte[] bytes = new byte[frameLength * frameSize]; 

最後,你可以在音頻讀取,傳遞的AudioInputStream空字節[]和捕捉適當的例外:

int result = 0; 
    try { 
     result = audioInputStream.read(bytes); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

轉換爲樣品和渠道

原始音頻數據不是非常有用的。它需要分解成渠道和樣本。從那裏,很容易畫樣品。

字節將被轉換爲採樣並以int表示。您需要一個容器來存儲所有渠道的樣本。因此,創建一個兩維的int [] []來引用每個通道的通道和採樣。您已經看到如何從AuduioInputStream獲取幀長度,並且您可以通過相同的方式獲取通道數量。這裏是初始化INT [] []的代碼:

int numChannels = audioInputStream.getFormat().getChannels(); 
int frameLength = (int) audioInputStream.getFrameLength(); 
int[][] toReturn = new int[numChannels][frameLength]; 

現在,需要通過字節[]進行迭代時,字節轉換成樣品,並放置在適當的信道的樣品中的INT [ ] []。字節[]按幀組織,這意味着您將讀取每個通道的樣本,而不是針對某一行中特定通道的所有樣本。因此,流動是通過渠道循環,直到字節[]已經完全重複添加樣本:

int sampleIndex = 0; 

for (int t = 0; t < eightBitByteArray.length;) { 
    for (int channel = 0; channel < numChannels; channel++) { 
     int low = (int) eightBitByteArray[t]; 
     t++; 
     int high = (int) eightBitByteArray[t]; 
     t++; 
     int sample = getSixteenBitSample(high, low); 
     toReturn[channel][sampleIndex] = sample; 
    } 
    sampleIndex++; 
}