前言

简单记录下一下Android开发中播放音频的几种方式。记录于此,方便自己查阅和回顾。

大部分内容摘抄于《Android中播放音频六种方式》,感谢。

正文

MediaPlayer

这种比较常见的。Android中MediaPlayer很强大,提供对音频或视频的各种方法,支持的格式也很多,同时方便用户定制开发。

 mMediaPlayer = new MediaPlayer();

设置监听

 mMediaPlayer.setOnCompletionListener(this);
 mMediaPlayer.setOnInfoListener(this);
 mMediaPlayer.setOnErrorListener(this);
 mMediaPlayer.setOnPreparedListener(this);
 mMediaPlayer.setOnSeekCompleteListener(this);

加载播放路径

这部分可以看《MediaPlayer播放不同来源的音频文件

//添加播放文件
mMediaPlayer.setDataSource(audioPath);

准备

mMediaPlayer.prepare();
//异步,推荐
mMediaPlayer.prepareAsync();

开始或暂停等

mMediaPlayer.start();
mMediaPlayer.pause();
mMediaPlayer.stop();

释放

mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;

AudioTrack

AudioTrack有两种模式,一种是边加载边播放(流模式streaming),另外一种是加载完播放(静态模式static)。

具体代码使用可以看《AudioTrack简单使用》。

流模式

流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景。

边播放边加载

适合下面几种情况:

  1. 音频文件过大

  2. 音频属性要求高,比如采样率高、深度大的数据

  3. 音频数据是实时产生的(推流,一次性只能发送或产生一部分)

静态模式

静态的言下之意就是数据一次性交付给接收方。

好处是简单高效,只需要进行一次操作就完成了数据的传递。

缺点也很明显,对于数据量较大的音频回放,显然无法胜任的。

通常只用于播放铃声、系统提醒等对内存小的操作。

SoundPool

SoundPool支持多个音频文件同时播放(当然,也是有上限的),延时短,比较适合短促和密集的场景,比如游戏开发中音效播放。

new方式

适用与5.0以下

SoundPool(int maxStreams, int streamType, int srcQuality)
从android5.0开始此方法被标记为过时,稍微说以下几个参数。
1.maxStreams :允许同时播放的流的最大值
2.streamType :音频流的类型描述,
               在Audiomanager中有种类型声明,游戏应用通常会使用流媒体音乐。
3. srcQuality:采样率转化质量
Builder方式
//设置描述音频流信息的属性
AudioAttributes abs = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
SoundPool mSoundPoll = new SoundPool.Builder()
        .setMaxStreams(100)   //设置允许同时播放的流的最大值
        .setAudioAttributes(abs)   //完全可以设置为null
        .build();
核心方法
// 几个load方法和上文提到的MediaPlayer基本一致,不做多的解释
int load(AssetFileDescriptor afd, int priority)
int load(Context context, int resId, int priority)
int load(String path, int priority)
int load(FileDescriptor fd, long offset, long length, int priority)
// 通过流id暂停播放
final void pause(int streamID)
// 播放声音,soundID:音频id; left/rightVolume:左右声道(默认1,1);loop:循环次数(-1无限循环);rate:播放速率(1为标准)
final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
//释放资源(很重要)
final void release()
//恢复播放
final void resume(int streamID)
//设置指定id的音频循环播放次数
final void setLoop(int streamID, int loop)
//设置加载监听(因为加载是异步的,需要监听加载,完成后再播放)
void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
//设置优先级(同时播放个数超过最大值时,优先级低的先被移除)
final void setPriority(int streamID, int priority)
//设置指定音频的播放速率,0.5~2.0(rate>1:加快播放,反之慢速播放)
final void setRate(int streamID, float rate)
//停止指定音频播放
final void stop(int streamID)
//卸载指定音频
final boolean unload(int soundID)
//暂停所有音频的播放
final void autoPause()
//恢复所有暂停的音频播放
final void autoResum()

也可以看《SoundPool的使用

AsyncPlayer

AsyncPlayer属于异步播放器。AsyncPlayer就是对MediaPlayer的一次简单的封装,其方法操作都在子线程中执行。

源码
\frameworks\base\media\java\android\media\AsyncPlayer.java
例子
AsyncPlayer asyncPlayer = new AsyncPlayer(TAG);

//网络资源(记得配置对应权限)
//Uri uri = Uri.parse("http://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3");
//内置raw资源
Uri uri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.biumall);

//播放uri
asyncPlayer.play(this, uri,true,new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build());
//停止
asyncPlayer.stop();

AsyncPlayer只适合简单的异步播放,不能控制进度,只能开始或停止播放。如果播放时再次调用play()方法,AsyncPlayer会停止当前播放,开始新的播放。

注意

为啥说[如果播放时再次调用play()方法,AsyncPlayer会停止当前播放,开始新的播放],这看了下面源码就知道为啥。

private void startSound(Command cmd) {
    try {
        //调用播放就是调用startSound()
        //然后这里重新new了一个MediaPlayer
        MediaPlayer player = new MediaPlayer();
        player.setAudioAttributes(cmd.attributes);
        player.setDataSource(cmd.context, cmd.uri);
        player.setLooping(cmd.looping);
        player.prepare();
        player.start();
        //若之前的mPlayer不为null,直接release(),也就是之前播放的停止并释放了。
        if (mPlayer != null) {
            mPlayer.release();
        }
        mPlayer = player;
        long delay = SystemClock.uptimeMillis() - cmd.requestTime;
        if (delay > 1000) {
            Log.w(mTag, "Notification sound delayed by " + delay + "msecs");
        }
    }
    catch (Exception e) {
        Log.w(mTag, "error loading sound for " + cmd.uri, e);
    }
}

JetPlayer

在Android中,还提供了对Jet播放的支持,Jet是由OHA联盟成员SONiVOX开发的一个交互音乐引擎。其包括两部分:JET播放器和JET引擎。JET常用于控制游戏的声音特效,采用MIDI(Musical Instrument Digital Interface)格式。

源码
\frameworks\base\media\java\android\media\JetPlayer.java
例子

主要方式

//获取JetPlayer播放器
JetPlayer mJet = JetPlayer.getJetPlayer() ;
// 清空分段队列,并清除所有要进行播放的剪辑。
boolean clearQueue()  //每次播放前,记得做一次清空操作
// 加载jet文件的方法
boolean loadJetFile(String path)
   boolean loadJetFile(AssetFileDescriptor afd)
// 开始播放
boolean play()
// 暂停播放
boolean pause()
// 释放资源
void release()
// 指定jet队列的播放序列(调用play()前需要调用此方法)
boolean queueJetSegment(int segmentNum, int libNum, int repeatCount, int transpose, int muteFlags, byte userID)

demo

private void jetPlayer() {
    // 获取JetPlayer播放器
    JetPlayer mJet = JetPlayer.getJetPlayer();
    //清空播放队列
    mJet.clearQueue();
    //绑定事件监听
    mJet.setEventListener(new JetPlayer.OnJetEventListener() {
        //播放次数记录
        int playNum = 1;
        
        @Override
        public void onJetEvent(JetPlayer player, short segment, byte track, byte channel, byte controller, byte value) {
        }

        @Override
        public void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount) {
        }

        @Override
        public void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments) {
        }

        @Override
        public void onJetPauseUpdate(JetPlayer player, int paused) {
            if (playNum == 2) {
                playNum = -1;
                //释放资源,并关闭jet文件
                player.release();
                player.closeJetFile();
            } else {
                playNum++;
            }
        }
    });
    //加载资源
    mJet.loadJetFile(getResources().openRawResourceFd(R.raw.biumall));
    byte sSegmentID = 0;
    //指定播放序列
    mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
    mJet.queueJetSegment(1, 0, 1, 0, 0, sSegmentID);
    //开始播放
    mJet.play();
}

Ringtone

Ringtone为铃声、通知和其他类似声音提供快速播放的方法,这里还不得不提到一个管理类”RingtoneManager”,提供系统铃声列表检索方法,并且,Ringtone实例需要从RingtoneManager获取。

RingtoneManager
 // 两个构造方法
RingtoneManager(Activity activity)
RingtoneManager(Context context)
// 获取指定声音类型(铃声、通知、闹铃等)的默认声音的Uri
static Uri getDefaultUri(int type)
// 获取系统所有Ringtone的cursor
Cursor getCursor()
// 获取cursor指定位置的Ringtone uri
Uri getRingtoneUri(int position)
// 判断指定Uri是否为默认铃声
static boolean isDefault(Uri ringtoneUri)
//获取指定uri的所属类型
static int getDefaultType(Uri defaultRingtoneUri)
//将指定Uri设置为指定声音类型的默认声音
static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri)
例子
 /**
 * 播放来电铃声的默认音乐
*/
private void playRingtoneDefault(){
    Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) ;
    Ringtone mRingtone = RingtoneManager.getRingtone(this,uri);
    mRingtone.play();
}

/**
* 随机播放一个Ringtone(有可能是提示音、铃声等)
*/
private void ShufflePlayback(){
	RingtoneManager manager = new RingtoneManager(this) ;
	Cursor cursor = manager.getCursor();
	int count = cursor.getCount() ;
	int position = (int)(Math.random()*count) ;
	Ringtone mRingtone = manager.getRingtone(position) ;
	mRingtone.play();
}

两个权限

<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

当然,Ringtone可以播放本地或网络资源等。

//raw内置资源
Uri uri2 = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.biumall);
Ringtone mRingtone = RingtoneManager.getRingtone(this, uri2);
mRingtone.play();

参考文章

  1. Android中播放音频六种方式

  2. MediaPlayer播放不同来源的音频文件

  3. AudioTrack简单使用

  4. SoundPool的使用

相关文章

暂无评论

none
暂无评论...