Android应用开发学习笔记之播放音频

时间:2022-09-02 05:52:56

作者:刘昊昱 

博客: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<StringString> 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播放音频文件的具体例子,该程序运行效果如下:

Android应用开发学习笔记之播放音频

主布局文件内容如下:

<?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播放音频的实例,该程序运行效果如下图所示,可以同时播放多个音频:

Android应用开发学习笔记之播放音频

先来看主布局文件,其内容如下:

<?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;
}
}