Android中播发声音目前主要采用MediaPlayer、SoundPool两种方式。MediaPlayer比较适合播放时间较长、文件大的音频文件.MediaPlayer存在如下的缺点:资源占用量高,延迟时间长、不支持多外音频同时播放。SoundPool的优点刚好可以弥补MediaPlayer的缺点,资源占用少,反应延迟小,还支持自行设置声音品质,音量,播放比率等。但是使用SoundPool要注意以下问题:1.SoundPool最大只能申请1M内存的空间,我们只能播发一些短小的声音片段;2.SoundPool中提供的pause、stop方法不推荐使用,可能会使你的应用程序莫名其妙终止。只建议在以下情况使用:1.程序中的短暂提示音(按键、消息提示音等);2.游戏中密集而短暂的声音。
1.创建SoundPool对象
private void intSoundPool() {
mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
mSoundIDs = new HashMap<Integer, Integer>();
mSoundIDs.put(1, mSoundPool.load(this, R.raw.praise1, 1));
mSoundIDs.put(2, mSoundPool.load(this, R.raw.praise2, 1));
mSoundIDs.put(3, mSoundPool.load(this, R.raw.praise3, 1));
}
SoundPool的构造函数定义如下(Android源码):
/**
* Constructor. Constructs a SoundPool object with the following
* characteristics:
*
* @param maxStreams the maximum number of simultaneous streams for this
* SoundPool object
* @param streamType the audio stream type as described in AudioManager
* For example, game applications will normally use
* {@link AudioManager#STREAM_MUSIC}.
* @param srcQuality the sample-rate converter quality. Currently has no
* effect. Use 0 for the default.
* @return a SoundPool object, or null if creation failed
* @deprecated use {@link SoundPool.Builder} instead to create and configure a
* SoundPool instance
*/
public SoundPool(int maxStreams, int streamType, int srcQuality) {
this(maxStreams,
new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
}
第一个参数maxStreams表示SoundPool能同时播放的声音最大数量,切忌合理使用,写的太大后会报“AudioFlinger could not create track, status: -12……”, 一旦报了这个错,就听不到声音了;
第二个参数streamType表示SoundPool播放的声音类型,通常我们设置为AudioManager.STREAM_MUSIC;
第三个参数srcQuality,该参数已失效,设置为0即可。
SoundPool通过load方法来加载声音(Android源码):
/**
* Load the sound from the specified APK resource.
*
* Note that the extension is dropped. For example, if you want to load
* a sound from the raw resource file "explosion.mp3", you would specify
* "R.raw.explosion" as the resource ID. Note that this means you cannot
* have both an "explosion.wav" and an "explosion.mp3" in the res/raw
* directory.
*
* @param context the application context
* @param resId the resource ID
* @param priority the priority of the sound. Currently has no effect. Use
* a value of 1 for future compatibility.
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(Context context, int resId, int priority) {
return mImpl.load(context, resId, priority);
}
参数都很好理解分别对应上下文、资源ID、优先级(已失效,设置为1即可);
返回一个id,我们需要把该id保存下来,在之后调用play、unload方法时作为参数使用。当调用load方法的时候,实际就是把音效加载到了 SoundPool中,此时返回的streamId其实就是该音效在SoundPool中的id。这个id从1递增,要注意的是,不要超过 256 这个临界点。也就是说,第257个声音加载进去后,调用play方法其实是播不出来的,说不定还会挤掉一些前面加载好的声音。这个256的限制通过查看SDK源码基本就能了解清楚,它底层就那么实现的,用一个类似堆栈来存。
2.使用SoundPool播放声音
private void playSound(int type) {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
float audioMaxVolum = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); // 获取设备最大音量
float audioCurrentVolum = am.getStreamVolume(AudioManager.STREAM_MUSIC); // 获取设备当前音量
float audioRatio = audioCurrentVolum / audioMaxVolum; // 获取当前音量系数
mSoundPool.play(mSoundIDs.get(type), audioRatio, audioRatio, 1, 0, 1); //播发声音
startPraiseAnim();
}
SoundPool调用play方法进行声音播放(Android源码):
/**
* Play a sound from a sound ID.
*
* Play the sound specified by the soundID. This is the value
* returned by the load() function. Returns a non-zero streamID
* if successful, zero if it fails. The streamID can be used to
* further control playback. Note that calling play() may cause
* another sound to stop playing if the maximum number of active
* streams is exceeded. A loop value of -1 means loop forever,
* a value of 0 means don't loop, other values indicate the
* number of repeats, e.g. a value of 1 plays the audio twice.
* The playback rate allows the application to vary the playback
* rate (pitch) of the sound. A value of 1.0 means play back at
* the original frequency. A value of 2.0 means play back twice
* as fast, and a value of 0.5 means playback at half speed.
*
* @param soundID a soundID returned by the load() function
* @param leftVolume left volume value (range = 0.0 to 1.0)
* @param rightVolume right volume value (range = 0.0 to 1.0)
* @param priority stream priority (0 = lowest priority)
* @param loop loop mode (0 = no loop, -1 = loop forever)
* @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
* @return non-zero streamID if successful, zero if failed
*/
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
return mImpl.play(
soundID, leftVolume, rightVolume, priority, loop, rate);
}
参数soundID是我们调用load方法时返回的对应id;
参数leftVolume、rightVolume分别对应左右声道声音大小,0~1.0的系数;
参数 priority是声音流优先级,最小值为0;
参数loop是否循环,0:否,-1:是;
参数rate是播放速率,1.0:正常,0.5~2.0慢速或加快;
返回值,返回非零的id表示播放成功,否则返回0表示播放失败。
3.SoundPool的资源释放
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
mSoundPool.release();
super.onDestroy();
}
调用SoundPool的release释放所有加载的声音资源,一定要记得。public final boolean unload(int soundID)方法卸载声音。
4.关于unload方法和release方法
如果你音效多,也不要指望unload方法来清除掉一些音效后再load新的进去,虽然unload后音效卸载了,但是前面分给它在SoundPool里面的Id可没有释放掉,也就是说这个时候你load新的进去只会在后面继续累加,然后累加多了就超过256了,然后就就听不到声音,然后就没有然后了。要想彻底清掉前面的音效请使用release方法,它会连内存中占用的资源一起释放掉。
5.load需要一点点时间,load后不要马上unload,load —play–unload的做法并不可取,不要load太大的音效,它只会申请1M的内存空间。SoundPool出错后通常会看到retuen的值是0。