4.音頻模組(Audio)
音頻模組編程從來都是一個複雜的話題。這裡不打算用到一些進階複雜的音頻處理手段,主要是播放一些背景音樂。在書寫代碼前,讓我們瞭解一下音訊基礎知識。
採樣率:定義了每秒從連續訊號中提取並組成離散訊號的採樣個數,採樣率越高音質越好,單位用赫茲(Hz)來表示,CD一般是44.1KHz。對於每個採樣系統會分配一定儲存位(bit數)來表達聲波的聲波振幅狀態,稱之為採樣解析度或採樣精度,每增加1個bit,表達聲波振幅的狀態數就翻一翻,並且增加6db的動態範圍態,1個2bit的數位音頻系統資料表達千種狀態,即12db的動態範圍,以此類推。如16bit能夠表達65536種狀態,24bit可以表達多達16777216種狀態。動態範圍是指聲音從最弱到最強的變化範圍,人耳的聽覺範圍通常是20HZ~20KHZ。高的採樣率意味著更多的儲存空間。比如60s的聲音,採樣率8KHz、8bits,大約0.5M,採樣率44KHz、16bits,超過5M,普通的3分鐘的流行歌曲,將會超過15M。
為了即不降低品質有不太佔據空間,很多比較好的壓縮方法被提出來。比如MP3s 和OGGs格式就是網路中比較流行的壓縮格式。
可以看到3min的歌曲佔了不少空間。當我們播放遊戲的後台音樂時,我們可以把音頻流化而不是積極式載入到記憶體。通常背景音樂只有一個,因此只需要到磁碟載入一次即可。
對於一些短的音效,比如爆炸聲和槍的射擊聲,情況有所不同。這些短的音效經常會同時被調用多次,從磁碟對每個執行個體流化這些音效不是一個好的辦法。幸運的是,短的音效並不佔用太多的記憶體空間,因此只需把這些音效提前讀入到記憶體即可,然後可以直接地同時播放這些音效。
因此我們的代碼需要提供如下功能:
我們需要一種方法載入音頻檔案,用於流化播放(Music)和記憶體播放(Sound),同時提供控制播放功能。
相應的介面有三個,Audio、Music和Sound,代碼如下。
Audio介面Audio.java
package com.badlogic.androidgames.framework;
public interface Audio {
public Music newMusic(String filename);
public Sound newSound(String filename);
}
Audio介面建立新的Music和 Sound執行個體。一個Music執行個體表示一個流音頻檔案,一個Sound執行個體表示一個儲存在記憶體中的短的音效。方法 Audio.newMusic()和Audio.newSound()都是以檔案名稱作為參數並拋出IOException以防檔案載入失敗(例如檔案不存在或者檔案損壞等情況)。
Music介面Music.jva
package com.badlogic.androidgames.framework;
public interface Music {
public void play();
public void stop();
public void pause();
public void setLooping(boolean looping);
public void setVolume(float volume);
public boolean isPlaying();
public boolean isStopped();
public boolean isLooping();
public void dispose();
}
Music介面有點複雜,包含了播放音樂流、暫訂和停止、迴圈播放、音量控制(從0到1的浮點數)方法。當然,裡面還有一些getter方法,用來擷取當前音樂執行個體的狀態。當我們不再需要Music 執行個體時, 我們可以銷毀它(dispose方法),這會關閉系統資源,即流化的音頻檔案。
Sound介面Sound.java
package com.badlogic.androidgames.framework;
public interface Sound {
public void play(float volume);
public void dispose();
}
Sound介面比較簡單,只包含play()和dispose()方法。前者以指定的音量為輸入參數,我們可以在任何我們需要的時候播放音效。後者在我們不許Sound執行個體時,我們需要銷毀它以釋放它佔用的記憶體空間。