目录
前言
简单记录下一下Android开发中播放音频的几种方式。记录于此,方便自己查阅和回顾。
大部分内容摘抄于《》,感谢。
正文
MediaPlayer
mMediaPlayer = new MediaPlayer();
设置监听
mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnInfoListener(this); mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnSeekCompleteListener(this);
加载播放路径
这部分可以看《》
//添加播放文件 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)。
具体代码使用可以看《》。
流模式
流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景。
边播放边加载
适合下面几种情况:
音频文件过大
音频属性要求高,比如采样率高、深度大的数据
音频数据是实时产生的(推流,一次性只能发送或产生一部分)
静态模式
静态的言下之意就是数据一次性交付给接收方。
好处是简单高效,只需要进行一次操作就完成了数据的传递。
缺点也很明显,对于数据量较大的音频回放,显然无法胜任的。
通常只用于播放铃声、系统提醒等对内存小的操作。
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()
也可以看《》
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();
参考文章
《》
《》
《》
《