package com.ydl.media.audio import android.content.Context import android.content.IntentFilter import android.media.AudioManager import android.os.Handler import android.os.Looper import com.tencent.bugly.Bugly.applicationContext import com.ydl.media.audio.enums.PlayModeEnum import com.ydl.media.audio.manager.AudioFocusManager import com.ydl.media.audio.manager.MediaSessionManager import com.ydl.media.audio.manager.NotifyManager import com.ydl.media.audio.model.Music import com.ydl.media.audio.receiver.NoisyAudioStreamReceiver import com.ydl.media.audio.utils.PlayProgressUtil import com.ydl.ydlcommon.event.MeditationFloatStopEvent import com.ydl.ydlcommon.utils.LogUtil import com.yidianling.common.tools.ToastUtil import de.greenrobot.event.EventBus import tv.danmaku.ijk.media.player.IMediaPlayer import tv.danmaku.ijk.media.player.IjkMediaPlayer import java.io.IOException import java.util.* import kotlin.collections.ArrayList /** * Created by haorui on 2019-10-27 . * Des: */ class AudioPlayer private constructor() { private var context: Context? = null private var audioFocusManager: AudioFocusManager? = null var mediaPlayer: IMediaPlayer? = null private var handler: Handler? = null private var noisyReceiver: NoisyAudioStreamReceiver? = null private var noisyFilter: IntentFilter? = null private val musicList: MutableList<Music> = ArrayList() private val listeners = ArrayList<OnPlayerEventListener>() private var state = STATE_IDLE private val mPublishRunnable = object : Runnable { override fun run() { if (isPlaying) { val current = (mediaPlayer!!.currentPosition * 1.0).toFloat() val du = mediaPlayer!!.duration.toFloat() val percent = (current * 100 / du).toInt() //保存进度 if (autoSaveProgress) { PlayProgressUtil.saveProgress( context, playMusic!!.path, (if (percent == 99 || percent == 100) 0 else current.toInt()) ) } for (listener in listeners) { listener.onPublish(percent, current.toLong()) } } handler!!.postDelayed(this, TIME_UPDATE) } } val audioSessionId: Int get() = if (mediaPlayer != null) mediaPlayer!!.audioSessionId else 0 val currentPosition: Long get() = if (isPlaying || isPausing) { mediaPlayer!!.currentPosition } else { 0 } val playMusic: Music? get() = if (musicList.isEmpty()) { null } else musicList[playPosition] val isPlaying: Boolean get() = state == STATE_PLAYING val isPausing: Boolean get() = state == STATE_PAUSE val isPreparing: Boolean get() = state == STATE_PREPARING val isIdle: Boolean get() = state == STATE_IDLE private var playPosition: Int = 0 get() { if (field < 0 || field >= musicList.size) { field = 0 } return field } /** * 设置播放模式 * 默认为列表循环 */ var playMode = PlayModeEnum.LIST_LOOP /** * 是否自动保存播放进度 */ var autoSaveProgress = false /** * 是否显示通知栏 */ var isShowNotify = false private object SingletonHolder { val instance = AudioPlayer() } /** * 初始化播放器 */ fun init(context: Context) { this.context = context.applicationContext audioFocusManager = AudioFocusManager(context) try { mediaPlayer = IjkMediaPlayer().also { it.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1) it.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1) } } catch (e: Exception) { LogUtil.e(e.message) } mediaPlayer!!.setOnPreparedListener { // TODO: 2022/8/4 startPlayer 和 onPrepared 调用顺序不对 if (isPreparing) { if (autoSaveProgress) { //自动播放 val time = PlayProgressUtil.getProgress(context, playMusic!!.path) if (time > 0) { startPlayer() seekTo(position = time.toLong()) } else { startPlayer() } } else { startPlayer() } } for (listener in listeners) { listener.onPrepared(mediaPlayer!!.duration) } } mediaPlayer!!.setOnBufferingUpdateListener { _, percent -> for (listener in listeners) { listener.onBufferingUpdate(percent) } } mediaPlayer!!.setOnCompletionListener { if (autoSaveProgress) { PlayProgressUtil.saveProgress(applicationContext, playMusic!!.path, 0) } for (listener in listeners) { listener.onComplete() } when (playMode) { PlayModeEnum.SINGLE -> { resetPlayer() playMode = PlayModeEnum.LIST_LOOP } PlayModeEnum.LIST -> { if (playPosition >= musicList.lastIndex) pausePlayer() else { next() } } else -> next() } } handler = Handler(Looper.getMainLooper()) noisyReceiver = NoisyAudioStreamReceiver() noisyFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) } /** * 添加播放监听 */ fun addOnPlayEventListener(listener: OnPlayerEventListener) { if (!listeners.contains(listener)) { listeners.add(listener) } } /** * 移除播放监听 */ fun removeOnPlayEventListener(listener: OnPlayerEventListener) { listeners.remove(listener) } /** * 添加播放列表 */ fun addPlayList(music: ArrayList<Music>) { if (music.isEmpty()) { return } musicList.clear() musicList.addAll(music) autoSaveProgress = false } /** * 单曲模式播放音乐 */ fun singlePlay(music: Music, isAutoSaveProgress: Boolean = false) { musicList.clear() musicList.add(music) playMode = PlayModeEnum.SINGLE autoSaveProgress = isAutoSaveProgress load(0) } /** * 单曲循环模式播放音乐 */ fun singleCirclePlay(music: Music) { musicList.clear() musicList.add(music) playMode = PlayModeEnum.SINGLE_LOOP autoSaveProgress = false load(0) } /** * 添加后自动播放 */ fun addAndPlay(music: Music) { var position = musicList.indexOf(music) if (position < 0) { musicList.add(music) position = musicList.size - 1 } load(position) } /** * 播放第一首 */ fun play() { load(0) } /** * 加载指定索引的音乐 */ fun load(index: Int) { if (musicList.isEmpty()) return var position = index if (position < 0) { position = musicList.size - 1 } else if (position >= musicList.size) { position = 0 } listeners.forEach { if (!it.onPreLoad(position)) return } playPosition = position val music = playMusic try { mediaPlayer!!.reset() mediaPlayer!!.dataSource = music!!.path mediaPlayer!!.prepareAsync() state = STATE_PREPARING for (listener in listeners) { listener.onLoad(music) } if (isShowNotify) { NotifyManager.get().showPlay(music) MediaSessionManager.get().updateMetaData(music) MediaSessionManager.get().updatePlaybackState() } } catch (e: IOException) { e.printStackTrace() ToastUtil.toastShort("当前歌曲无法播放") } } /** * 从列表里移除播放音乐 */ fun delete(position: Int) { musicList.removeAt(position) if (playPosition > position) { playPosition -= 1 } else if (playPosition == position) { if (isPlaying || isPreparing) { playPosition -= 1 next() } else { resetPlayer() for (listener in listeners) { listener.onLoad(playMusic!!) } } } } /** * 暂停或者播放音乐 */ fun playOrPause() { when { isPreparing -> { resetPlayer() } isPlaying -> { pausePlayer() } isPausing -> { startPlayer() } else -> { load(playPosition) } } } /** *开始播放 */ fun startPlayer() { if (!isPreparing && !isPausing) { return } if (audioFocusManager!!.requestAudioFocus()) { mediaPlayer!!.start() state = STATE_PLAYING handler!!.post(mPublishRunnable) if (isShowNotify) { NotifyManager.get().showPlay(playMusic) MediaSessionManager.get().updatePlaybackState() } context!!.registerReceiver(noisyReceiver, noisyFilter) for (listener in listeners) { listener.onStartPlay() } } val event = MeditationFloatStopEvent(show = false, stop = true, time = null) EventBus.getDefault().post(event) } /** * 暂停播放器 */ @JvmOverloads fun pausePlayer(abandonAudioFocus: Boolean = true) { if (!isPlaying) { return } mediaPlayer!!.pause() state = STATE_PAUSE handler!!.removeCallbacks(mPublishRunnable) if (isShowNotify) { NotifyManager.get().showPause(playMusic) MediaSessionManager.get().updatePlaybackState() } context!!.unregisterReceiver(noisyReceiver) if (abandonAudioFocus) { audioFocusManager!!.abandonAudioFocus() } for (listener in listeners) { listener.onPausePlay() } } /** * 停止播放器 */ fun resetPlayer() { if (isIdle) { return } pausePlayer() musicList.clear() mediaPlayer!!.reset() state = STATE_IDLE } fun getNextPosition(position: Int): Int { return when (playMode) { PlayModeEnum.SHUFFLE -> Random().nextInt(musicList.size) PlayModeEnum.SINGLE_LOOP -> position PlayModeEnum.LIST_LOOP -> if (position >= musicList.lastIndex) 0 else position + 1 PlayModeEnum.LIST -> if (position >= musicList.lastIndex) -1 else position + 1 else -> position + 1 } } /** * 下一首 */ fun next() { if (musicList.isEmpty()) { return } val position = getNextPosition(playPosition) if (-1 == position) { ToastUtil.toastShort("暂无内容") return } load(position) } /** * 上一首 */ fun prev() { if (musicList.isEmpty()) { return } val position = when (playMode) { PlayModeEnum.SHUFFLE -> Random().nextInt(musicList.size) PlayModeEnum.SINGLE_LOOP -> playPosition PlayModeEnum.LIST_LOOP -> if (playPosition <= 0) musicList.lastIndex else playPosition - 1 PlayModeEnum.LIST -> if (playPosition <= 0) -1 else playPosition - 1 else -> playPosition - 1 } if (-1 == position) { ToastUtil.toastShort("暂无内容") return } load(position) } /** * 跳转到指定的时间位置 * @param percent 百分比 * @param position 时间点 */ fun seekTo(percent: Int = -1, position: Long = -1) { if (isPlaying || isPausing) { val duration = getDuration() val currentPercent: Int val currentPosition = if (position != -1L) { val pos = when { position > duration -> duration position < 0 -> 0L else -> position } currentPercent = (pos / duration).toInt() pos } else { currentPercent = when { percent > 100 -> 100 percent < 0 -> 0 else -> percent } currentPercent * duration / 100 } mediaPlayer!!.seekTo(currentPosition) MediaSessionManager.get().updatePlaybackState() if (autoSaveProgress) { PlayProgressUtil.saveProgress( context, musicList[playPosition].coverPath, currentPosition.toInt() ) } for (listener in listeners) { listener.onPublish(currentPercent, currentPosition) } } } /** * 获取播放列表 */ fun getMusicList(): List<Music> { return musicList } /** * 获取音乐时长 */ fun getDuration(): Long { return mediaPlayer?.duration ?: 0 } companion object { // TODO: 2022/8/4 缺少完成状态和出错状态,目前看代码逻辑是 // TODO: 2022/8/4 正常播放完,到下一首加载前都是 STATE_PLAYING 状态,如果播放链接出错,则到下一首加载前是 STATE_PREPARING 状态 private val STATE_IDLE = 0 private val STATE_PREPARING = 1 private val STATE_PLAYING = 2 private val STATE_PAUSE = 3 private val TIME_UPDATE = 300L fun get(): AudioPlayer { return SingletonHolder.instance } } /** * 设置倍速播放 */ fun setSpeed(speed: Float) { (mediaPlayer as? IjkMediaPlayer)?.setSpeed(speed) } /** * 获取播放倍速 */ fun getSpeed(): Float = (mediaPlayer as? IjkMediaPlayer)?.getSpeed(0f) ?: 1f }