android音效的加载方式
下面是MediaPlayer和SoundPool類的對(duì)比特性:
1.soundpool可以播一些短的反應(yīng)速度要求高的聲音,?
比如游戲中的爆破聲,而mediaplayer適合播放長(zhǎng)點(diǎn)的。?
2. SoundPool載入音樂(lè)文件使用了獨(dú)立的線程,不會(huì)阻塞UI主線程的操作。但是這里如果音效文件過(guò)大沒(méi)有載入完成,我們調(diào)用play方法時(shí)可能產(chǎn)生嚴(yán) 重的后果,這里Android SDK提供了一個(gè)SoundPool.OnLoadCompleteListener類來(lái)幫助我們了解媒體文件是否載入完成,我們重載 onLoadComplete(SoundPool soundPool, int sampleId, int status) 方法即可獲得。?
3. 從上面的onLoadComplete方法可以看出該類有很多參數(shù),比如類似id,是的SoundPool在load時(shí)可以處理多個(gè)媒體一次初始化并放入內(nèi)存中,這里效率比MediaPlayer高了很多。?
4. SoundPool類支持同時(shí)播放多個(gè)音效,這對(duì)于游戲來(lái)說(shuō)是十分必要的,而MediaPlayer類是同步執(zhí)行的只能一個(gè)文件一個(gè)文件的播放。
?
1. 游戲音效SoundPool
游 戲中會(huì)根據(jù)不同的動(dòng)作 , 產(chǎn)生各種音效 , 這些音效的特點(diǎn)是短暫(叫聲,爆炸聲可能持續(xù)不到一秒) , 重復(fù)(一個(gè)文件不斷重復(fù)播放) , 并且同時(shí)播放(比如打怪時(shí)怪的叫聲 , 和技能釋放的聲音需要同時(shí)播放) , 即時(shí)(技能用處之后聲音馬上隨著玩家操作發(fā)出,不能有延遲).
MediaPlayer會(huì)占用大量的系統(tǒng)資源 , 并且不能同時(shí)播放 , 并且無(wú)法實(shí)現(xiàn)即時(shí)音效 , 這里引入了一個(gè)新的類 -- SoundPool , 這個(gè)類完全滿足上面提出的四點(diǎn)要求 , 可以無(wú)延時(shí)播放游戲中的短暫音效 .
2. 相關(guān)API介紹
(1) SoundPool
構(gòu)造方法 : SoundPool(int maxStreams, int streamType, int srcQuality) ;
參數(shù)解析 :?
maxStream : 該參數(shù)是定義最多能同時(shí)播放的多少音效 .
streamType : 該參數(shù)定義音頻類型 , 游戲中一般設(shè)置為AudioManager.STREAM_MUSIC .
srcQuality : 該參數(shù)用來(lái)設(shè)置音頻質(zhì)量 , 這個(gè)參數(shù)目前沒(méi)有作用 , 這里設(shè)置為 0;
加載音頻文件方法 :?int?load(Context context, int resId, int priority);
參數(shù)解析 :
context : 上下文對(duì)象;
resId : 要加載的資源文件 , 即R.raw.music...
priority : 優(yōu)先級(jí)別 , 這里沒(méi)有作用 , 設(shè)置為1.
播放音效方法 : int play(int soundId, float leftVolume, float rightVolume, int priority, int loop, float rate);
參數(shù)解析 :
soundId : 這個(gè)id不是資源id , 指的是利用load方法加載資源文件返回的id值 , 這個(gè)要區(qū)別清楚.
leftVolume : 左聲道的音量 , 這個(gè)音量是一個(gè) 0 ~ 1的數(shù) , 這個(gè)小數(shù)是當(dāng)前音量/最大音量的結(jié)果;
rightVolume : 右聲道的音量 , 這個(gè)音量與左聲道的音量是同一種音量;
priority : 優(yōu)先級(jí)參數(shù) , 0為最低, 這里設(shè)置為1;
loop : 音效循環(huán)的次數(shù) , 0為不循環(huán) , -1為永遠(yuǎn)循環(huán);
rate : 音效回放的速度 , 這個(gè)值是在0.5~2.0f之間 , 1f是正常速度;
?
暫停音效播放方法 :?pause(int streamId);
參數(shù)streamId :?這個(gè)參數(shù)是play()方法執(zhí)行完之后的返回值 , 這個(gè)返回值是正在播放的音效的一個(gè)標(biāo)識(shí) , 對(duì)正在播放的音效進(jìn)行操作的時(shí)候 , 就需要這個(gè)標(biāo)識(shí)來(lái)對(duì)其進(jìn)行操作;
通知音效播放方法 : stop(int streamId) , 這個(gè)參數(shù)與上面的pause()方法中的streamId參數(shù)是一個(gè)效果.
?
(2)AudioManager
獲取方法 : AudioManager對(duì)象時(shí)系統(tǒng)服務(wù), 可以通過(guò)調(diào)用上下文對(duì)象的getSystemService(Context.AUDIO_SERVICE)獲取 , 注意獲取到之后 , 需要將對(duì)象墻磚為AudioManager對(duì)象才可以使用.
eg : AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
利用AudioManager獲取當(dāng)前音量的方法 : float currVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
利用AudioManager獲取當(dāng)前系統(tǒng)最大音量方法 : float maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
使用這兩個(gè)音量就可以計(jì)算出運(yùn)行SoundPool音效的音量 , 當(dāng)前音量 / 系統(tǒng)最大音量 , 結(jié)果就是soundPool.play()方法中需要傳入的音量 ;?
3. 程序代碼
public class MainActivity extends Activity implements OnClickListener { private SoundPool soundPool; private HashMap<Integer, Integer> hashMap; private int currStreamId; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initSoundPool(); } private void initSoundPool() { soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0); hashMap = new HashMap<Integer, Integer>(); hashMap.put(1, soundPool.load(getApplicationContext(), R.raw.musictest, 1)); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bt_play: play(1, 0); Toast.makeText(getApplicationContext(), "播放即時(shí)音效", Toast.LENGTH_LONG).show(); break; case R.id.bt_stop: soundPool.stop(currStreamId); Toast.makeText(getApplicationContext(), "暫停播放", Toast.LENGTH_LONG).show(); break; default: break; } } private void play(int sound, int loop) { AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); float currVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); float maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); float volume = currVolume / maxVolume; currStreamId = soundPool.play(hashMap.get(sound), volume, volume, 1, loop, 1.0f); System.out.println(currStreamId); } }4. 程序的注意點(diǎn)
- 音效文件需要放在res的raw下.
- SoundPool播放的音效要小于7秒 , 否則會(huì)出現(xiàn)加載失敗的現(xiàn)象;
- 播放的大小盡量不超過(guò)1M,太大會(huì)影響播放;
- 在Android平臺(tái)上使用的即時(shí)文件越小越好 , 必要的時(shí)候可以降低采樣頻率或者將立體聲改為單聲道;
- 都說(shuō)MediaPlayer比較耗資源,在一樣的情況下(文件一致),只使用一個(gè)MediaPlayer的對(duì)象的reset(),prepare(),start()這些方法速度的慢也體驗(yàn)不出來(lái)。SoundPool和MediaPlayer都可以使用,且相對(duì)而言MediaPlayer要穩(wěn)定些;
- 當(dāng)調(diào)用load方法的時(shí)候?qū)嶋H就是把音效加載到了 SoundPool中,此時(shí)返回的streamId其實(shí)就是該音效在SoundPool中的Id,這個(gè)ID從0還是1來(lái)著(有點(diǎn)記不清了) 遞增,不過(guò)要注意的是,不要超過(guò) ?256 ?這個(gè)臨界點(diǎn)。也就是說(shuō)第257個(gè)聲音加載進(jìn)去后,調(diào)用play方法其實(shí)是播不出來(lái)的,說(shuō)不定還會(huì)擠掉一些前面加載好的聲音。這個(gè)256的限制通過(guò)查看SDK源碼基本就能了解清楚,它底層就那么實(shí)現(xiàn)的,用一個(gè)類似堆棧來(lái)存;
- 在創(chuàng)建的時(shí)候 maxStream這個(gè)參數(shù)代表能夠同時(shí)播放的最大音效數(shù),這里切忌合理使用,寫(xiě)的太大后會(huì)報(bào)AudioFlinger could not ?create track, status: -12 。。。。一旦報(bào)了這個(gè)錯(cuò),你就聽(tīng)不到聲音了;
- 如果你音效多,也不要指望unload方法來(lái)清除掉一些音效后再load新的進(jìn)去,雖然unload后音效卸載了,但是前面分給它在SoundPool里面的Id可沒(méi)有釋放掉,也就是說(shuō)這個(gè)時(shí)候你load新的進(jìn)去只會(huì)在后面繼續(xù)累加,然后累加多了就超過(guò)256了,然后就就聽(tīng)不到聲音,然后就沒(méi)有然后了。要想徹底清掉前面的音效請(qǐng)使用release方法,它會(huì)連內(nèi)存中占用的資源一起釋放掉;
- load需要一點(diǎn)點(diǎn)時(shí)間,load后不要馬上unload,load ---play--unload的做法并不可取,不要load太大的音效,它只會(huì)申請(qǐng)1M的內(nèi)存空間。SoundPool出錯(cuò)后通常會(huì)看到return的值是0
SoundPool —— 適合短促且對(duì)反應(yīng)速度比較高的情況(游戲音效或按鍵聲等)
下面介紹SoundPool的創(chuàng)建過(guò)程:
1. 創(chuàng)建一個(gè)SoundPool?(構(gòu)造函數(shù))
public SoundPool(int maxStream, int streamType, int srcQuality)?maxStream —— 同時(shí)播放的流的最大數(shù)量
streamType —— 流的類型,一般為STREAM_MUSIC(具體在AudioManager類中列出)
srcQuality —— 采樣率轉(zhuǎn)化質(zhì)量,當(dāng)前無(wú)效果,使用0作為默認(rèn)值
初始化一個(gè)實(shí)例:
SoundPool soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);?
創(chuàng)建了一個(gè)最多支持5個(gè)流同時(shí)播放的,類型標(biāo)記為音樂(lè)的SoundPool。
2. 加載音頻資源?
可以通過(guò)四種途徑來(lái)記載一個(gè)音頻資源:int load(AssetFileDescriptor afd, int priority)?
通過(guò)一個(gè)AssetFileDescriptor對(duì)象
int load(Context context, int resId, int priority)?
通過(guò)一個(gè)資源ID
int load(String path, int priority)?
通過(guò)指定的路徑加載
int load(FileDescriptor fd, long offset, long length, int priority)?
通過(guò)FileDescriptor加載
*API中指出,其中的priority參數(shù)目前沒(méi)有效果,建議設(shè)置為1。?
一個(gè)SoundPool能同時(shí)管理多個(gè)音頻,所以可以通過(guò)多次調(diào)用load函數(shù)來(lái)記載,如果記載成功將返回一個(gè)非0的soundID?,用于播放時(shí)指定特定的音頻。
int?soundID1?= soundPool.load(this, R.raw.sound1, 1);
if(soundID1?==0){
??? // 記載失敗
}else{
?? // 加載成功
}
int?soundID2?= soundPool.load(this, R.raw.sound2, 1);
...?
這里加載了兩個(gè)流,并分別記錄了返回的soundID?。
需要注意的是,?
流的加載過(guò)程是一個(gè)將音頻解壓為原始16位PCM數(shù)據(jù)的過(guò)程,由一個(gè)后臺(tái)線程來(lái)進(jìn)行處理異步,所以初始化后不能立即播放,需要等待一點(diǎn)時(shí)間。
3. 播放控制?
有以下幾個(gè)函數(shù)可用于控制播放:final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)?
播放指定音頻的音效,并返回一個(gè)streamID?。
??????? priority —— 流的優(yōu)先級(jí),值越大優(yōu)先級(jí)高,影響當(dāng)同時(shí)播放數(shù)量超出了最大支持?jǐn)?shù)時(shí)SoundPool對(duì)該流的處理;
??????? loop —— 循環(huán)播放的次數(shù),0為值播放一次,-1為無(wú)限循環(huán),其他值為播放loop+1次(例如,3為一共播放4次).
??????? rate —— 播放的速率,范圍0.5-2.0(0.5為一半速率,1.0為正常速率,2.0為兩倍速率)
final void pause(int streamID)?
暫停指定播放流的音效(streamID?應(yīng)通過(guò)play()返回)。
final void resume(int streamID)?
繼續(xù)播放指定播放流的音效(streamID?應(yīng)通過(guò)play()返回)。
final void stop(int streamID)?
終止指定播放流的音效(streamID?應(yīng)通過(guò)play()返回)。
這里需要注意的是,?
1.play()函數(shù)傳遞的是一個(gè)load()返回的soundID——指向一個(gè)被記載的音頻資源?,如果播放成功則返回一個(gè)非0的streamID——指向一個(gè)成功播放的流?;同一個(gè)soundID?可以通過(guò)多次調(diào)用play()而獲得多個(gè)不同的streamID?(只要不超出同時(shí)播放的最大數(shù)量);
2.pause()、resume()和stop()是針對(duì)播放流操作的,傳遞的是play()返回的streamID?;
3.play()中的priority參數(shù),只在同時(shí)播放的流的數(shù)量超過(guò)了預(yù)先設(shè)定的最大數(shù)量是起作用,管理器將自動(dòng)終止優(yōu)先級(jí)低的播放流。如果存在多個(gè)同樣優(yōu)先級(jí)的流,再進(jìn)一步根據(jù)其創(chuàng)建事件來(lái)處理,新創(chuàng)建的流的年齡是最小的,將被終止;
4.無(wú)論如何,程序退出時(shí),手動(dòng)終止播放并釋放資源是必要的。
4. 更多屬性設(shè)置?
其實(shí)就是paly()中的一些參數(shù)的獨(dú)立設(shè)置:final void setLoop(int streamID, int loop)?
設(shè)置指定播放流的循環(huán).
final void setVolume(int streamID, float leftVolume, float rightVolume)?
設(shè)置指定播放流的音量.
final void setPriority(int streamID, int priority)?
設(shè)置指定播放流的優(yōu)先級(jí),上面已說(shuō)明priority的作用.
final void setRate(int streamID, float rate)?
設(shè)置指定播放流的速率,0.5-2.0.
5. 釋放資源?
可操作的函數(shù)有:final boolean unload(int soundID)?
卸載一個(gè)指定的音頻資源.
final void release()?
釋放SoundPool中的所有音頻資源.
下面對(duì)以上進(jìn)行總結(jié):
一個(gè)SoundPool可以:
1.管理多個(gè)音頻資源,通過(guò)load()函數(shù),成功則返回非0的soundID;
2.同時(shí)播放多個(gè)音頻,通過(guò)play()函數(shù),成功則返回非0的streamID;
3.pause()、resume()和stop()等操作是針對(duì)streamID(播放流)的;
4.當(dāng)設(shè)置為無(wú)限循環(huán)時(shí),需要手動(dòng)調(diào)用stop()來(lái)終止播放;
5.播放流的優(yōu)先級(jí)(play()中的priority參數(shù)),只在同時(shí)播放數(shù)超過(guò)設(shè)定的最大數(shù)時(shí)起作用;
6.程序中不用考慮(play觸發(fā)的)播放流的生命周期,無(wú)效的soundID/streamID不會(huì)導(dǎo)致程序錯(cuò)誤。
參考1:http://blog.csdn.net/qduningning/article/details/8680575
參考2:http://www.open-open.com/lib/view/open1390879650507.html參考3:http://blog.csdn.net/xiaominghimi/article/details/6101737
總結(jié)
以上是生活随笔為你收集整理的android音效的加载方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: iec 61508安全标准_成功符合IE
- 下一篇: 基于MATLAB机器人工具箱的KUKA