Android音频播放之SoundPool 详解

/ Android / 没有评论 / 1548浏览

Android音频播放之SoundPool 详解

SoundPool —— 适合短促且对反应速度比较高的情况(游戏音效或按键声等).下面介绍SoundPool的创建过程

1. 创建一个SoundPool (构造函数)

public SoundPool(int maxStream, int streamType, int srcQuality) 

初始化一个实例:

SoundPool soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0); 

创建了一个最多支持5个流同时播放的,类型标记为音乐的SoundPool。

2. 加载音频资源

可以通过四种途径来记载一个音频资源:

int load(AssetFileDescriptor afd, int priority) 

通过一个AssetFileDescriptor对象

int load(Context context, int resId, int priority) 

通过一个资源ID

int load(String path, int priority) 

通过指定的路径加载

int load(FileDescriptor fd, long offset, long length, int priority) 

通过FileDescriptor加载

API中指出,其中的priority参数目前没有效果,建议设置为1。

一个SoundPool能同时管理多个音频,所以可以通过多次调用load函数来记载,如果记载成功将返回一个非0的soundID ,用于播放时指定特定的音频。

int soundID1 = soundPool.load(this, R.raw.sound1, 1);
if(soundID1 ==0){
    // 记载失败
}else{
   // 加载成功
}
int soundID2 = soundPool.load(this, R.raw.sound2, 1);
... 

这里加载了两个流,并分别记录了返回的soundID 。

需要注意的是, 流的加载过程是一个将音频解压为原始16位PCM数据的过程,由一个后台线程来进行处理异步,所以初始化后不能立即播放,需要等待一点时间

3. 播放控制

有以下几个函数可用于控制播放:

final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) 

播放指定音频的音效,并返回一个streamID 。

final void pause(int streamID) 

暂停指定播放流的音效(streamID 应通过play()返回)。

final void resume(int streamID) 

继续播放指定播放流的音效(streamID 应通过play()返回)。

final void stop(int streamID) 

终止指定播放流的音效(streamID 应通过play()返回)。

这里需要注意的是:

  1. play()函数传递的是一个load()返回的soundID——指向一个被记载的音频资源 ,如果播放成功则返回一个非0的streamID——指向一个成功播放的流 ;同一个soundID 可以通过多次调用play()而获得多个不同的streamID (只要不超出同时播放的最大数量);
  2. pause()、resume()和stop()是针对播放流操作的,传递的是play()返回的streamID ;
  3. play()中的priority参数,只在同时播放的流的数量超过了预先设定的最大数量是起作用,管理器将自动终止优先级低的播放流。如果存在多个同样优先级的流,再进一步根据其创建事件来处理,新创建的流的年龄是最小的,将被终止;
  4. 无论如何,程序退出时,手动终止播放并释放资源是必要的

## 4. 更多属性设置

其实就是paly()中的一些参数的独立设置:

final void setLoop(int streamID, int loop) 

设置指定播放流的循环.

final void setVolume(int streamID, float leftVolume, float rightVolume)

设置指定播放流的音量.

final void setPriority(int streamID, int priority) 

设置指定播放流的优先级,上面已说明priority的作用.

final void setRate(int streamID, float rate) 

设置指定播放流的速率,0.5-2.0.

5. 释放资源

可操作的函数有:

final boolean unload(int soundID) 

卸载一个指定的音频资源.

final void release() 

释放SoundPool中的所有音频资源.

下面对以上进行总结:一个SoundPool可以:

  1. 管理多个音频资源,通过load()函数,成功则返回非0的soundID;
  2. 同时播放多个音频,通过play()函数,成功则返回非0的streamID;
  3. pause()、resume()和stop()等操作是针对streamID(播放流)的;
  4. 当设置为无限循环时,需要手动调用stop()来终止播放;
  5. 播放流的优先级(play()中的priority参数),只在同时播放数超过设定的最大数时起作用;
  6. 程序中不用考虑(play触发的)播放流的生命周期,无效的soundID/streamID不会导致程序错误。

示例代码:

SoundPool sp;
HashMap<Integer, Integer> sounddata;
Context mcontext;
Boolean isLoaded;

//初始化声音
public void InitSound() {
                 sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
                 sounddata = new HashMap<Integer, Integer>();
                 sounddata.put(1, sp.load(this, R.raw.mp31, 1));
                 sounddata.put(2, sp.load(this, R.raw.mp32, 1));
          sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener(){
                @Override 
                public void onLoadComplete(SoundPool sound,int sampleId,int status){
                    isLoaded=true;
                    Toast.makeText(mcontext, 
                            "音效加载完成!",
                            Toast.LENGTH_SHORT);
              }
          });
        }

……
    public void playSound(int sound, int number) {
        AudioManager am = (AudioManager) this
                .getSystemService(Context.AUDIO_SERVICE);
        float audioMaxVolumn = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        float volumnCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC);
        float volumnRatio = volumnCurrent / audioMaxVolumn;

        sp.play(sounddata.get(sound),   
                volumnRatio,// 左声道音量  
                volumnRatio,// 右声道音量  
                1, // 优先级  
                number,// 循环播放次数  
                1);// 回放速度,该值在0.5-2.0之间 1为正常速度  
    }

……

//2.播放声音
 if(isLoaded==true)
    playSound(1,1);

经常有人在使用Soundpool时会报错

可以从以下几个方面解决此问题:

  1. 因为android系统一个设备只允许同时有32个音效文件播放,所以要你在程序中控制同时播放的音频数。并且在Activity被覆盖到下面或者锁屏时对资源进行回收,即调用Soundpool对象的Release()方法。

For audio, there's a hard limit of 32 active AudioTrack objects per device (not per app: you need to share those 32 with rest of the system), and AudioTrack is used internally beneath SoundPool, ToneGenerator, MediaPlayer, native audio based on OpenSL ES, etc. But the actual AudioTrack limit is < 32; it depends more on soft factors such as memory, CPU load, etc. Also note that the limiter in the Android audio mixer does not currently have dynamic range compression, so it is possible to clip if you have a large number of active sounds and they're all loud.

  1. 每个音频文件大小要小于100k;可以修改音频码率来压缩音频大小,把 128kbps改成32kbps ;

  2. 每次播放一次,soundpool.play方法有个参数是控制重复播放次数的,将之设置为0,即只播放一次;

资源