游戏的音效由于比较特殊,使用MediaPlay会占用太多的资源,所以使用SoundPlay,它将音频文件预先保存在内存中,所以在调用的时候会大大节省CPU资源,其他的一些原因网上一大堆。
使用:
转自:http://www.cnblogs.com/plokmju/p/android_SoundPool.html
SoundPool
SoundPool(声音池),所处于"android.media.SoundPool"包下,主要用于播放一些较短的声音片段,支持从程序的资源或文件系统加载。与MediaPlayer相比,SoundPool的优势在于CPU的资源占用量低、反应延迟小,并且可以加载多个音频到SoundPool中,通过资源ID来管理。另外SoundPool还支持执行设置声音的品质、音量、播放比率等参数。
SoundPool提供一个构造函数,以下是它的完整签名:
SoundPool(int maxStreams,int streamType,int srcQuality)
通过上面的构造函数即可完成SoundPool的初始化,第一个参数为音频池最多支持装载多少个音频,就是音频池的大小;第二个参数指定声音的类型,在AudioManager类中以常量的形式定义,一般指定为AudioManager.STREAM_MUSIC即可;第三个参数为音频的质量,默认为0,这个参数为预留参数,现在没有实际意义,为扩展预留字段,一般传0即可。
对于一个音频池,涉及到音频的加载、播放、暂停、继续、释放资源等操作,SoundPool也为我们提供了相应的方法,其底层也是用C++编写的native方法。以下介绍一些常用的SoundPool方法:
- int load(Context context,int resId,int priority):从一个文件夹raw下装载一段音频资源,返回值为音频资源在SoundPool的ID。
- int load(String path,int priority):从一个资源文件的路径装载一段音频资源,返回值为音频资源在SoundPool的ID。
- final int play(int soundID,float leftVolume,float rightVolume,int priority,int loop,float rate):根据资源ID,播放一段音频资源。
- final void pause(int streamID):根据装载资源ID,暂停音频资源的播放。
- final void resume(int streamID):根据装载资源ID,继续播放暂停的音频资源。
- final void stop(int streamID):根据装载资源ID,停止音频资源的播放。
- final boolean unload(int soundID) :从音频池中卸载音频资源ID为soundID的资源。
- final void release():释放音频池资源。
上面方法无疑Load()和play()是最重要的,Load()具有多种重载方法,从参数名就可以看出是什么意思。这里讲解一下play()方法,soundID参数为资源ID;leftVolume和rightVolume个参数为左右声道的音量,从大到小取0.0f~1.0f之间的值;priority为音频质量,暂时没有实际意义,传0即可;loop为循环次数,0为播放一次,-1为无线循环,其他正数+1为播放次数,如传递3,循环播放4次;rate为播放速率,从大到小取0.0f~2.0f,1.0f为正常速率播放。
在使用load()装载音频的时候需要注意,load()方法是一个异步的方法,也就是说,在播放音频的时候,很可能此段音频还没有装载到音频池中,这里可以借助SoundPool的一个装载完成的监听事件SoundPool.setOnLoadCompleteListener来保证装载完成在播放声音。SoundPool.setOnLoadCompleteListener()需要实现一个SoundPool.OnLoadCompleteListener接口,其中需要实现onLoadComplete()方法,一下是onLoadComplete()方法的完整签名:
onLoadComplete(SoundPool soundPool, int sampleId, int status)
- soundPool:当前触发事件的声音池。
- sampleId:当前装载完成的音频资源在音频池中的ID。
- status:状态码,展示没有意义,为预留参数,会传递0。
源码:
package com.example.jigsaw.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.SoundPool;
import android.media.SoundPool.OnLoadCompleteListener;
import android.util.Log;
import android.util.SparseIntArray;
/**
* 播放游戏背景音乐和音效
* @author LuZiQi
*
*/
public class GameMusic {
private SoundPool mPool;
private SparseIntArray mArray;
private Map<String , Integer> map;
private Context mContext;
private static GameMusic instance;
public static GameMusic getInstanc(Context context){
if(instance == null)
instance = new GameMusic(context);
return instance;
}
private GameMusic(Context context){
mContext = context;
}
/**
* 初始化背景音乐和音效
* @param list 音效音乐的文件名集合
* @param backMusic 背景音乐的文件名
* @throws IOException
*/
public void init(ArrayList<String> list) throws IOException{
// 初始化音频池 参数:池中有多少个音频(音频池的大小), 第二个是类型(使用AudioManager.STREAM_MUSIC就行了),第三个预留参数0。
mPool = new SoundPool(list.size(), AudioManager.STREAM_MUSIC, 0);
map = new HashMap<String, Integer>();
// 初始化SparseIntArray 它是android特有的稀有数集合,替代HASHMAP最佳办法,如果一个集合相当多的元素并不是经常使用,只使用其中的少数元素,使用它可以节省资源
mArray = new SparseIntArray();
// 注册加载完毕监听器,加载完成时
mPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
Log.i("luziqi", "ok");
}
});
// 将音效音乐加载到音频池
for (int i = 0; i < list.size(); i++) {
// 由于音乐放在assets文件夹下 所以要用AssetManeger来获取文件
AssetFileDescriptor afd = mContext.getAssets().openFd(list.get(i));
// SoundPool的load方法会返回一个该资源在池中的对应ID,这里吧他记录到SparseIntArray数组中。
mArray.put(i+1, mPool.load(afd, 1));
map.put(list.get(i), i+1);
}
}
public void play(String fileName){
if(mPool == null)
return;
int priority = map.get(fileName);
// 从音频池中取出对应的音频文件播放 参数:load时返回的ID , 左声道音量 , 右声道音量 , 优先级 , 重复次数(0是不重复,-1是一只重复),播放速率 0~2.0 1.0是正常速率。
mPool.play(priority, 0.7f, 0.7f, 1, 0, 1.0f);
}
public void close(){
if(mPool == null)
return;
mPool.release();
}
}