这是一个音乐播放器,支持:速率调节,音调调节,调节采样率,这三个功能基于soundTouch开源项目,解决了Android 6.0之前不能调节播放速率的问题。项目地址:https://github.com/zhencheng11/AudioPlayerMaster.git
体验APK:https://github.com/zhencheng11/AudioPlayerMaster/blob/master/App.apk
由于公司项目需要做一个倍速播放的音乐播放器,我们知道,使用Android 自带的MediaPlayer的在Android 6.0之前的是不支持倍速播放的,我看过很多关于音频播放的开源项目,找到soundTouch能够改变音频播放速度,但是没有一个完整的能封装成播放器的。于是,我结合AudioTrack+MediaExtractor+MediaCodec+SoundTouch封装成一个音乐播放器。因为之前都是用Android 自带的MediaPlayer进行播放,使用这些新技术我花了很多的时间去了解他们的使用。
先看项目截图:UI有点粗糙,见谅。
以下介绍项目重要功能和技术:
之前我写过一篇过于Service的使用,这里正好用到里面的一些知识,就是startService和bindService的混合使用,我们知道,音频播放都是在后台进行播放,我们推出页面的时候需要音乐也继续在后台播放,所以我们需要开启一个服务来播放,但是同时,我们需要在显示播放界面的时候也能看到播放状态,于是我们需要绑定到这个服务上面,监听回调后台音乐播放的状态展示给用户,这里我就不细说了,不明白的可以看我的博客【Android Service的使用】。
对于只要求正常播放的话,使用AudioTrack+MediaExtractor+MediaCodec就可以了,MediaExtractor用于加载资源,可以加载网络资源,本地资源,通过setDataSource()方法,设置要加载的资源;MediaCodec将MediaExtractor的流进行编码然后传给AudioTrack进行播放;MeAudioTrack用于播放,可以通过传入音频流就可以进行播放,内部方法为write()
方法,将音频流写入,再调用play()
方法就可以进行播放。对于需要设置播放倍速,设置音调来说,以上三个类的组合是不行的,需要借助于soundTouch它是一个开源项目,专门针对调节播放速率,调节音调,原理是将MediaCodec解码后的流传给soundTouch,soundTouch进行进一步的变化之后,再传给AudioTrack进行播放。
private static synchronized native final void setTempo(int track, float tempo);
通过soundTouch的本地方法setTempo()
设置播放速率,这种情况下,只是改变了播放的速度,它的音调并没有改变,传入自己需要的倍数就可以进行倍速播放,1倍速代表正常的倍数。
private static synchronized native final void setPitchSemi(int track, float pitchSemi);
通过传入音调数值,取值范围[-12-12]可以调节音调。
private static synchronized native final void setRate(int track, float rate);
通过setRate()
方法,rate的取值范围[-50-100],可以设置采样频率,它改变播放速度的同时也改变了音调,所以它是变速又变调
的。
通过Notification将播放状态显示在状态栏,需要注意的是系统适配问题
//Android 8.0以后
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(context.getPackageName(), "测试音频", NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(false);
channel.enableVibration(false);
channel.setVibrationPattern(new long[]{0});
channel.setSound(null, null);
manager.createNotificationChannel(channel);
builder.setChannelId(context.getPackageName());
} else {
builder.setVibrate(new long[]{0});
builder.setSound(null);
}
在Android 8.0以后,通知需要设置Channelld才能显示,需要用NotificationChannel
类来进行配置,还有设置声音和震动也需要用此类来进行配置。
在我们播放音频的时候,我们希望其他正在播放音频的软件暂停播放,这里就需要使用到获取焦点的这个类AudioManager
,通过AudioManager的requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)
来获取音频的焦点,这样,其他的音频也就会暂停播放了,当需要释放焦点时通过abandonAudioFocus(OnAudioFocusChangeListener l)
方法来释放焦点。
如上面的最后一个图,通过MediaSessionCompat设置联动媒体,跟我们自己的播放器同步,可以显示在锁屏页面。
创建MediaSessionCompat
/**
* 初始化并激活MediaSession
*/
private void setupMediaSession() {
mMediaSession = new MediaSessionCompat(context, TAG);
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
);
//设置播放监听
mMediaSession.setCallback(callback);
mMediaSession.setActive(true);
}
更新媒体状态
/**
* 更新播放状态,播放/暂停/拖动进度条时调用
*/
public void updatePlaybackState() {
int state = isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
mMediaSession.setPlaybackState(
new PlaybackStateCompat.Builder()
.setActions(MEDIA_SESSION_ACTIONS)
.setState(state, getCurrentPosition(), 1)
.build());
}
个更新媒体信息
/**
* 更新正在播放的音乐信息,切换歌曲时调用
*/
public void updateMetaData(Audio audio) {
if (audio == null) {
mMediaSession.setMetadata(null);
return;
}
Audio info = control.getAudio();
MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, info.getName())
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, info.getArtist())
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, info.getAlbum())
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, info.getArtist())
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, control.getDuration())
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getCoverBitmap(info));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getCount());
}
mMediaSession.setMetadata(metaData.build());
}
项目地址:https://github.com/zhencheng11/AudioPlayerMaster.git