作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz
Android支持常用音视频格式文件的播放,本文我们来学习怎样开发Android应用程序对音视频进行操作。
Android提供了MediaPlayer和SoundPool两个类能够用来播放音频,MediaPlayer类通常用来播放较大的文件,比如一首MP3歌曲,这种文件通常保存在SD卡上,而不是保存在资源文件中,当然如果有必要,也可以保存在资源文件中。SoundPool类通常用来播放体积较小的文件,比如游戏中的炸弹爆炸声,这种文件通常保存在资源文件目录res/raw中。MediaPlayer一次只能播放一个文件,而SoundPool可同时播放多个音频文件。
一、用MediaPlayer播放音频
下面我们先来怎样看用MediaPlayer来控制音频播放。
(1)关联MediaPlayer对象与音频文件
用MediaPlayer播放音频文件,首先需要做的是把MediaPlayer对象与音频文件相关联,关联有两种方式,一是直接使用MediaPlayer.create()函数创建关联好音频文件的MediaPlayer对象;二是先创建一个MediaPlayer对象,然后再使用MediaPlayer.setDataSource()关联音频文件。
我们先来看MediaPlayer.create(),create()函数有以下几种格式:
static MediaPlayer |
create(Context context, Uri uri, SurfaceHolder holder) Convenience method to create a MediaPlayer for a given Uri. |
static MediaPlayer |
create(Context context, int resid) Convenience method to create a MediaPlayer for a given resource id. |
static MediaPlayer |
create(Context context, Uri uri) Convenience method to create a MediaPlayer for a given Uri. |
例如:
MediaPlayer player =MediaPlayer.create(this, R.raw.r);
MediaPlayer player =MediaPlayer.create(this, Uri.parse(“http://www.music.com/song/1.mp3”));
注意在访问网络资源时,要获得网络权限,即在AndroidManifest.xml文件中增加如下语句:
<uses-permissionandroid:name="android.permission.INTERNET" />
MediaPlayer类有一个无参数的构造函数,我们可以使用这个构造函数创建一个MediaPlayer对象,再使用MediaPlayer.setDataSource()函数关联MediaPlayer对象与音频文件。
setDataSource()函数有如下几种形式:
void |
setDataSource(String path) Sets the data source (file-path or http/rtsp URL) to use. |
void |
setDataSource(Context context, Uri uri, Map<String, String> headers) Sets the data source as a content Uri. |
void |
setDataSource(Context context, Uri uri) Sets the data source as a content Uri. |
void |
setDataSource(FileDescriptor fd, long offset, long length) Sets the data source (FileDescriptor) to use. |
void |
setDataSource(FileDescriptor fd) Sets the data source (FileDescriptor) to use. |
需要注意的是,在使用setDataSource关联MediaPlayer对象和音频资源后,还不能直接对音频资源进行播放等操作,还需要调用MediaPlayer.prepare方法真正装载音频文件。
例如:
MediaPlayer player = new MediaPlayer();
try{
player.setDataSource(“/sdcard/s.wav”);
}catch(IOException e){
e.printStackTrace();
}
try{
player.prepare();
}catch(IOException e){
e.printStackTrace();
}
(2)使用MediaPlayer对音频资源进行控制操作
常用的控制函数如下:
MediaPlayer.start():开始播放音频或恢复播放已经暂停的音频。
MeidaPlayer.stop():停止正在播放的音频。
MediaPlayer.pause():暂停正在播放的音频。
(3)MediaPlayer播放实例
下面我们来看一个使用MediaPlayer播放音频文件的具体例子,该程序运行效果如下:
主布局文件内容如下:
<?xml version="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/hint" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10px" android:text="单击“播放”按钮播放音频" /> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:enabled="false" android:text="暂停" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:enabled="false" android:text="停止" /> </LinearLayout> </LinearLayout>
主Activity文件内容如下:
package com.liuhaoyu; import java.io.File; import android.app.Activity; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public classMainActivity extends Activity { private MediaPlayer player; private boolean isPause = false; private File file; private TextView hint; @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final Button button1 =(Button) findViewById(R.id.button1); final Button button2 =(Button) findViewById(R.id.button2); final Button button3 =(Button) findViewById(R.id.button3); hint = (TextView)findViewById(R.id.hint); file = new File("/sdcard/1.mp3"); if (file.exists()) { player = MediaPlayer .create(this, Uri.parse(file.getAbsolutePath())); }else{ hint.setText("要播放的音频文件不存在!"); button1.setEnabled(false); return; } player.setOnCompletionListener(new OnCompletionListener(){ @Override public voidonCompletion(MediaPlayer mp) { play(); } }); button1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { play(); if (isPause) { button2.setText("暂停"); isPause = false; } button2.setEnabled(true); button3.setEnabled(true); button1.setEnabled(false); } }); button2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (player.isPlaying() &&!isPause){ player.pause(); isPause = true; ((Button)v).setText("继续"); hint.setText("暂停播放音频..."); button1.setEnabled(true); }else{ player.start(); ((Button)v).setText("暂停"); hint.setText("继续播放音频..."); isPause = false; button1.setEnabled(false); } } }); button3.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { player.stop(); hint.setText("停止播放音频..."); button2.setEnabled(false); button3.setEnabled(false); button1.setEnabled(true); } }); } private void play() { try { player.reset(); player.setDataSource(file.getAbsolutePath()); player.prepare(); player.start(); hint.setText("正在播放音频..."); }catch(Exception e) { e.printStackTrace(); } } @Override protected void onDestroy() { if(player.isPlaying()){ player.stop(); } player.release(); super.onDestroy(); } }
二、用SoundPool播放音频
用MediaPlayer播放音频存在一些不足,例如,资源占用量较高、延迟时间较长、不支持多个音频同时播放等等。这些缺点决定了MediaPlayer在某些场合下不适合使用,例如在对时间精度要求相对较高的游戏开发中。游戏开发中我人经常要播放一些游戏音效(比如子弹爆炸,物体撞击等等),这些音效的特点是短促、密集、延迟程度小。这时,我们可以使用SoundPool代替MediaPlayer来播放这些音效。
SoundPool,顾名思义是声音池的意思,主要用于播放一些较短的声音片段,支持从程序的资源或文件系统加载。与MediaPlayer相比,SoundPool的优势在于CPU资源占用量低和反应延迟小。另外,SoundPool还支持自行设置声音的品质、音量、播放比特率等参数,支持通过ID对多个音频流进行管理。
SoundPool最大只能申请1M的内存空间,这就意味着我们只能用它来播放一些很短的声音片断,而不是用它来播放歌曲或者做游戏背景音乐。
使用SoundPool播放音频,首先需要创建SoundPool对象,然后加载需要播放的音频,最后调用SoundPool.play()函数播放音频。
(1) 创建一个SoundPool对象
SoundPool类构造函数格式如下:
SoundPool(int maxStreams, int streamType,int srcQuality)
第一个参数maxStreams指定该SoundPool最多可以同时播放多少个音频流。
第二个参数streamType指定音频流类型,音频流类型是在AudioManager类中定义的。
第三个参数srcQuality指定音频流的品质,默认值为0。
例如,要创建一个可同时播放10个音频流的SoundPool对象,可以使用下面的代码:
SoundPool soundpool = new SoundPool(10,AudioManager.STREAM_SYSTEM, 0);
(2) 加载所要播放的音频
创建了SoundPool对象后,可以调用SoundPool.load()函数来加载要播放的音频,其格式如下:
int |
load(AssetFileDescriptor afd, int priority) Load the sound from an asset file descriptor. |
int |
load(Context context, int resId, int priority) Load the sound from the specified APK resource. |
int |
load(String path, int priority) Load the sound from the specified path. |
int |
load(FileDescriptor fd, long offset, long length, int priority) Load the sound from a FileDescriptor. |
例如,如果要通过资源ID来加载音频文件audio.wav,可以使用下面的代码:
soundpool.load(this, R.raw.audio, 1);
(3) 播放音频
加载了音频文件后,就可以调用SoundPool.play()函数来播放指定的音频,其格式如下:
final int |
play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) Play a sound from a sound ID. |
参数soundID指定要播放的音频ID,该ID是由load()函数返回的。
参数leftVolume指定左声道的音量,取值范围是0.0 – 1.0。
参数rightVolume指定右声道的音量,取值范围是0.0 – 1.0。
参数priority指定播放音频的优先级,数值越大,优先级越高。
参数loop指定循环次数,0为不循环,-1为一直循环。
参数rate指定播放速率,正常为1,最低为0.5,最高为2。
(4) SoundPool播放实例
下面我们来看一个使用SoundPool播放音频的实例,该程序运行效果如下图所示,可以同时播放多个音频:
先来看主布局文件,其内容如下:
<?xml version="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Audio1" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Audio2" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Audio3" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Audio4" /> </LinearLayout>
下面看主Activity文件,其内容如下:
package com.liuhaoyu; import java.util.HashMap; import android.app.Activity; import android.media.AudioManager; import android.media.SoundPool; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public classMainActivity extends Activity { private SoundPool soundpool; private HashMap<Integer,Integer> soundmap= newHashMap<Integer, Integer>(); //创建一个HashMap对象 @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Buttonchimes = (Button) findViewById(R.id.button1); Buttonenter = (Button) findViewById(R.id.button2); Buttonnotify = (Button) findViewById(R.id.button3); Buttonringout = (Button) findViewById(R.id.button4); soundpool = new SoundPool(5,AudioManager.STREAM_SYSTEM, 0); //将要播放的音频流保存到HashMap对象中 soundmap.put(1, soundpool.load(this, R.raw.chimes, 1)); soundmap.put(2, soundpool.load(this, R.raw.enter, 1)); soundmap.put(3, soundpool.load(this, R.raw.notify, 1)); soundmap.put(4, soundpool.load(this, R.raw.ringout, 1)); soundmap.put(5, soundpool.load(this, R.raw.ding, 1)); //为各按钮添加单击事件监听器 chimes.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { soundpool.play(soundmap.get(1), 1, 1, 0, 0, 1); } }); enter.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { soundpool.play(soundmap.get(2), 1, 1, 0, 0, 1); } }); notify.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { soundpool.play(soundmap.get(3), 1, 1, 0, 0, 1); } }); ringout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { soundpool.play(soundmap.get(4), 1, 1, 0, 0, 1); } }); } @Override public boolean onKeyDown(int keyCode, KeyEventevent) { soundpool.play(soundmap.get(5), 1, 1, 0, 0, 1); return true; } }