作者:刘昊昱
博客:http://blog.****.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;
}
}