Android SoundPool 播放音效
Android 预设的多媒体框架 (multimedia framework) 是 OpenCore
Android 多媒体框架 OpenCore
OpenCore 的优点是兼顾了 跨平台的移植性,而且已经过多方验证,所以相对来说较為稳定;但是其缺点是过於庞大复杂, 需要耗费相当多的时间去维护
Android 2.0 引进了架构稍微简洁一点的 Stagefright ,当然没有完全抛弃 OpenCore,主要是做了一个 OMX 层,仅仅是对 OpenCore 的 omx-component 部分做了引用
Android 多媒体框架处于 Android 架构的第三层( Libraries ) 的 Media Framework
如果想知道 Android 这套多媒体框架支持什么类型的音视频数据可以访问 Supported Media Formats
SoundPool 播放音效
SoundPool 一般用来播放密集,急促而又短暂的音效, 比如特技音效:Duang~,游戏用得较多
我们可以使用 SoundPool 给 APP 添加诸如酷狗音乐进去的时候播放"哈喽,酷狗"
也可以使用 SoundPool 给 APP 添加收到推送 信息或者新的聊天信息,然后播放提示音
SoundPool 对象可以看作是一个可以从APK中导入资源 或者从文件系统中载入文件的样本集合
SoundPoll 利用 MediaPlayer 服务为音频解码为一个原始 16 位 PCM 流
这个特性使得应用程序可以进行流压缩,而无须忍受在播放音频时解压所带来的 CPU 负载和延时
SoundPool使用 音效池 的概念来管理多个 播放流 ,如果超过流的最大数目, SoundPool会基于优先级自动停止先前播放的流
另外,SoundPool还支持自行设置声音的品质、音量、 播放比率等参数
创建 SoundPool 实例
在 API 21 (Android 5.0) 之前可以使用下面这个构造方法
SoundPool(int maxStreams, int streamType, int srcQuality)
参数说明
参数 | 说明 |
---|---|
maxStreams | 指定支持多少个声音,SoundPool 对象中允许同时存在的最大流的数量 |
streamType | 指定声音类型,在 AudioManager 定义了以下几种流类型 STREAM_VOICE_CALL STREAM_SYSTEM STREAM_RING STREAM_MUSIC STREAM_ALARM |
srcQuality | 指定声音品质(采样率变换质量),一般直接设置为 0 |
但在 API 21(Android 5.0) 及以上版本,这个构造方法被废弃了,取而代之的是使用 SoundPool.Builder()
方法
SoundPool.Builder spb = new SoundPool.Builder(); spb.setMaxStreams(10); spb.setAudioAttributes(null); //转换音频格式 SoundPool sp = spb.build(); //创建SoundPool对象
因此在使用过程中要对系统的版本进行判断,以选择合适的方法
// 版本大于等于 21 (Anndroid 5.0) SoundPool sp = null; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { SoundPool.Builder spb = new SoundPool.Builder(); spb.setMaxStreams(10); spb.setAudioAttributes(null); //转换音频格式 sp = spb.build(); //创建SoundPool对象 } else { sp = SoundPool(10, AudioManager.STREAM_SYSTEM, 5); }
SoundPool 常用方法
-
load()
用于加载声音资源
load()
方法有 4 种变体,每种方法都会返回一个声音的 IDload(Context context, int resId, int priority) load(String path, int priority) load(FileDescriptor fd, long offset, long length, int priority) load(AssetFileDescriptor afd, int priority)
参数说明
参数 说明 context 上下文 resId 资源id priority 没什么用的一个参数,建议设置为1,保持和未来的兼容性 path 文件路径 FileDescriptor 文件描述符 AssetFileDescriptor 从 asset 目录读取某个资源文件,用法
AssetFileDescriptor descriptor = assetManager.openFd("biaobiao.mp3") -
play()
播放控制
play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
参数说明
参数 说明 soundID load() 返回的声音 ID 号 leftVolume 左声道音量设置 rightVolume 右声道音量设置 priority 指定播放声音的优先级,数值越高,优先级越大。 loop 指定是否循环
-1 表示无限循环
0 表示不循环
其它值表示要重复播放的次数rate 指定播放速率,播放速率的取值范围是 0.5 至 2.0
1.0 的播放率可以使声音按照其原始频率
2.0 的播放速率,可以使声音按照其原始频率的两倍播放
0.5的播放率,则播放速率是原始频率的一半 -
release()
释放资源
release() release(int soundID)
release() 方法用于释放所有 SoundPool 对象占据的内存和资源
范例
接下来我们就使用 SoundPool 来播放 "哆啦A梦" 的声音,演示效果如下
当点击按钮的时候会,会播放 "哆啦A梦" 的铃声
好好玩,很魔幻的声音
-
创建一个 空的 Android 项目
cn.twle.android.SoundPoll
-
在 res 目录上点右键,然后选择
Android resource directory
,在弹出的界面中Resource type
项选择raw
然后点击 OK -
在
app
目录上点右键,然后选择Folder->Assets Folder
创建assets
目录 -
下载音频文件 /static/i/android/biaobiao.mp3 并放到
res/raw
目录下 -
下载音频文件 /static/i/android/biaobiao.mp3 并放到
assets
目录下 -
修改
activity_main.xml
<?xml version="1.0" encoding="utf-8" ?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <Button android:id="@+id/btn_play1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放声音1" /> <Button android:id="@+id/btn_play2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放声音2" /> <Button android:id="@+id/btn_play3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放声音3" /> <Button android:id="@+id/btn_play4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放声音4" /> <Button android:id="@+id/btn_play5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放声音5" /> <Button android:id="@+id/btn_release" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="释放 SoundPool" /> </LinearLayout>
-
修改
MainActivity.java
创建SoundPool
实例并播放res/raw
目录下的biaobiao.mp3
文件package cn.twle.android.soundpoll; import android.content.res.AssetManager; import android.media.AudioManager; import android.media.SoundPool; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import java.util.HashMap; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private Button btn_play1; private Button btn_play2; private Button btn_play3; private Button btn_play4; private Button btn_play5; private Button btn_release; private AssetManager aManager; private SoundPool mSoundPool = null; private HashMap<Integer, Integer> soundID = new HashMap<Integer, Integer>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); aManager = getAssets(); try { initSP(); } catch (Exception e) { e.printStackTrace(); } bindViews(); } private void bindViews() { btn_play1 = (Button) findViewById(R.id.btn_play1); btn_play2 = (Button) findViewById(R.id.btn_play2); btn_play3 = (Button) findViewById(R.id.btn_play3); btn_play4 = (Button) findViewById(R.id.btn_play4); btn_play5 = (Button) findViewById(R.id.btn_play5); btn_release = (Button) findViewById(R.id.btn_release); btn_play1.setOnClickListener(this); btn_play2.setOnClickListener(this); btn_play3.setOnClickListener(this); btn_play4.setOnClickListener(this); btn_play5.setOnClickListener(this); btn_release.setOnClickListener(this); } private void initSP() throws Exception{ //设置最多可容纳5个音频流,音频的品质为5 mSoundPool = new SoundPool(5, AudioManager.STREAM_SYSTEM, 5); soundID.put(1, mSoundPool.load(this, R.raw.biaobiao, 1)); soundID.put(2 , mSoundPool.load(getAssets().openFd("biaobiao.mp3") , 1)); //需要捕获IO异常 soundID.put(3, mSoundPool.load(this, R.raw.biaobiao, 1)); soundID.put(4, mSoundPool.load(this, R.raw.biaobiao, 1)); soundID.put(5, mSoundPool.load(this, R.raw.biaobiao, 1)); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_play1: mSoundPool.play(soundID.get(1), 1, 1, 0, 0, 1); break; case R.id.btn_play2: mSoundPool.play(soundID.get(2), 1, 1, 0, 0, 1); break; case R.id.btn_play3: mSoundPool.play(soundID.get(3), 1, 1, 0, 0, 1); break; case R.id.btn_play4: mSoundPool.play(soundID.get(4), 1, 1, 0, 0, 1); break; case R.id.btn_play5: mSoundPool.play(soundID.get(5), 1, 1, 0, 0, 1); break; case R.id.btn_release: mSoundPool.release(); //回收SoundPool资源 break; } } }
当点击了最后一个按钮释放 SoundPool
后,此后如果再点击其它按钮就不会 Duang
SoundPool 支持的事件
SoundPool 支持 OnLoadCompleteListener
事件,用于监听声音文件是否加载完毕
我们可以给 SoundPool
实例添加该事件并重写 onLoadComplete()
mSoundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() { @Override public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { Toast.makeText(MainActivity.this,"加特技准备完毕~",Toast.LENGTH_SHORT).show(); } });