Fix Playback speed and pitch for PlaybackManager

This commit is contained in:
Prathamesh More 2022-05-14 12:13:55 +05:30
parent 1033711357
commit 0fd8ab0ade
6 changed files with 54 additions and 68 deletions

View file

@ -3,6 +3,7 @@ package code.name.monkey.retromusic.service
import code.name.monkey.retromusic.cast.CastHelper.toMediaInfo import code.name.monkey.retromusic.cast.CastHelper.toMediaInfo
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.playback.Playback import code.name.monkey.retromusic.service.playback.Playback
import code.name.monkey.retromusic.util.PreferenceUtil.playbackSpeed
import com.google.android.gms.cast.MediaLoadOptions import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaSeekOptions import com.google.android.gms.cast.MediaSeekOptions
import com.google.android.gms.cast.MediaStatus import com.google.android.gms.cast.MediaStatus
@ -18,6 +19,7 @@ class CastPlayer(castSession: CastSession) : Playback,
init { init {
remoteMediaClient?.registerCallback(this) remoteMediaClient?.registerCallback(this)
remoteMediaClient?.setPlaybackRate(playbackSpeed.toDouble().coerceIn(0.5, 2.0))
} }
private var isActuallyPlaying = false private var isActuallyPlaying = false
@ -93,6 +95,10 @@ class CastPlayer(castSession: CastSession) : Playback,
override fun setCrossFadeDuration(duration: Int) {} override fun setCrossFadeDuration(duration: Int) {}
override fun setPlaybackSpeedPitch(speed: Float, pitch: Float) {
remoteMediaClient?.setPlaybackRate(speed.toDouble().coerceIn(0.5, 2.0))
}
override fun onStatusUpdated() { override fun onStatusUpdated() {
when (remoteMediaClient?.playerState) { when (remoteMediaClient?.playerState) {
MediaStatus.PLAYER_STATE_IDLE -> { MediaStatus.PLAYER_STATE_IDLE -> {

View file

@ -2,12 +2,10 @@ package code.name.monkey.retromusic.service
import android.animation.Animator import android.animation.Animator
import android.content.Context import android.content.Context
import android.content.Intent
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.AudioManager import android.media.AudioManager
import android.media.MediaPlayer import android.media.MediaPlayer
import android.media.PlaybackParams import android.media.PlaybackParams
import android.media.audiofx.AudioEffect
import android.os.PowerManager import android.os.PowerManager
import android.util.Log import android.util.Log
import androidx.core.net.toUri import androidx.core.net.toUri
@ -20,7 +18,6 @@ import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.AudioFader.Companion.createFadeAnimator import code.name.monkey.retromusic.service.AudioFader.Companion.createFadeAnimator
import code.name.monkey.retromusic.service.playback.Playback import code.name.monkey.retromusic.service.playback.Playback
import code.name.monkey.retromusic.service.playback.Playback.PlaybackCallbacks import code.name.monkey.retromusic.service.playback.Playback.PlaybackCallbacks
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.PreferenceUtil.playbackPitch import code.name.monkey.retromusic.util.PreferenceUtil.playbackPitch
import code.name.monkey.retromusic.util.PreferenceUtil.playbackSpeed import code.name.monkey.retromusic.util.PreferenceUtil.playbackSpeed
@ -144,6 +141,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
} }
hasDataSource = true hasDataSource = true
} else { } else {
completion(true)
mIsInitialized = true mIsInitialized = true
} }
} }
@ -170,7 +168,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
player.setAudioAttributes( player.setAudioAttributes(
AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build() AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build()
) )
player.setPlaybackSpeedPitch(PreferenceUtil.playbackSpeed, PreferenceUtil.playbackPitch) player.setPlaybackSpeedPitch(playbackSpeed, playbackPitch)
player.setOnPreparedListener { player.setOnPreparedListener {
player.setOnPreparedListener(null) player.setOnPreparedListener(null)
completion(true) completion(true)
@ -329,8 +327,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
getNextPlayer()?.let { player -> getNextPlayer()?.let { player ->
val nextSong = MusicPlayerRemote.nextSong val nextSong = MusicPlayerRemote.nextSong
if (nextSong != null) { if (nextSong != null) {
setDataSourceImpl(player, setDataSourceImpl(player, nextSong.uri.toString()) { success ->
MusicUtil.getSongFileUri(nextSong.id).toString()) { success ->
// Switch to other player (Crossfade) only if next song exists // Switch to other player (Crossfade) only if next song exists
if (success) switchPlayer() if (success) switchPlayer()
} }
@ -358,16 +355,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
override fun setPlaybackSpeedPitch(speed: Float, pitch: Float) { override fun setPlaybackSpeedPitch(speed: Float, pitch: Float) {
getCurrentPlayer()?.setPlaybackSpeedPitch(speed, pitch) getCurrentPlayer()?.setPlaybackSpeedPitch(speed, pitch)
} getNextPlayer()?.setPlaybackSpeedPitch(speed, pitch)
private fun MediaPlayer.setPlaybackSpeedPitch(speed: Float, pitch: Float) {
if (hasMarshmallow()) {
val wasPlaying: Boolean = isPlaying
playbackParams = PlaybackParams().setSpeed(speed).setPitch(pitch)
if (!wasPlaying) {
if (isPlaying) pause()
}
}
} }
companion object { companion object {
@ -375,4 +363,14 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
} }
} }
internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Main) internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Main)
fun MediaPlayer.setPlaybackSpeedPitch(speed: Float, pitch: Float) {
if (hasMarshmallow()) {
val wasPlaying: Boolean = isPlaying
playbackParams = PlaybackParams().setSpeed(speed).setPitch(pitch)
if (!wasPlaying) {
if (isPlaying) pause()
}
}
}

View file

@ -14,19 +14,12 @@
package code.name.monkey.retromusic.service package code.name.monkey.retromusic.service
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.MediaPlayer import android.media.MediaPlayer
import android.media.MediaPlayer.OnCompletionListener import android.media.MediaPlayer.OnCompletionListener
import android.media.PlaybackParams
import android.net.Uri import android.net.Uri
import android.os.PowerManager import android.os.PowerManager
import android.util.Log import android.util.Log
import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.util.VersionUtils.hasMarshmallow
import code.name.monkey.retromusic.PLAYBACK_PITCH
import code.name.monkey.retromusic.PLAYBACK_SPEED
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.extensions.uri import code.name.monkey.retromusic.extensions.uri
@ -41,7 +34,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil.playbackSpeed
* @author Andrew Neal, Karim Abou Zeid (kabouzeid) * @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/ */
class MultiPlayer internal constructor(private val context: Context) : Playback, class MultiPlayer internal constructor(private val context: Context) : Playback,
MediaPlayer.OnErrorListener, OnCompletionListener, OnSharedPreferenceChangeListener { MediaPlayer.OnErrorListener, OnCompletionListener {
private var mCurrentMediaPlayer = MediaPlayer() private var mCurrentMediaPlayer = MediaPlayer()
private var mNextMediaPlayer: MediaPlayer? = null private var mNextMediaPlayer: MediaPlayer? = null
private var callbacks: PlaybackCallbacks? = null private var callbacks: PlaybackCallbacks? = null
@ -52,6 +45,10 @@ class MultiPlayer internal constructor(private val context: Context) : Playback,
override var isInitialized = false override var isInitialized = false
private set private set
init {
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
}
/** /**
* @param song The song object you want to play * @param song The song object you want to play
* @return True if the `player` has been prepared and is ready to play, false otherwise * @return True if the `player` has been prepared and is ready to play, false otherwise
@ -89,12 +86,12 @@ class MultiPlayer internal constructor(private val context: Context) : Playback,
} else { } else {
player.setDataSource(path) player.setDataSource(path)
} }
setPlaybackSpeedPitch(player)
player.setAudioAttributes(AudioAttributes.Builder() player.setAudioAttributes(AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) .setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build() .build()
) )
player.setPlaybackSpeedPitch(playbackSpeed, playbackPitch)
player.setOnPreparedListener { player.setOnPreparedListener {
player.setOnPreparedListener(null) player.setOnPreparedListener(null)
completion(true) completion(true)
@ -198,8 +195,6 @@ class MultiPlayer internal constructor(private val context: Context) : Playback,
if (mNextMediaPlayer != null) { if (mNextMediaPlayer != null) {
mNextMediaPlayer?.release() mNextMediaPlayer?.release()
} }
PreferenceManager.getDefaultSharedPreferences(context)
.unregisterOnSharedPreferenceChangeListener(this)
} }
/** /**
@ -298,9 +293,6 @@ class MultiPlayer internal constructor(private val context: Context) : Playback,
override val audioSessionId: Int override val audioSessionId: Int
get() = mCurrentMediaPlayer.audioSessionId get() = mCurrentMediaPlayer.audioSessionId
/**
* {@inheritDoc}
*/
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
isInitialized = false isInitialized = false
mCurrentMediaPlayer.release() mCurrentMediaPlayer.release()
@ -311,9 +303,6 @@ class MultiPlayer internal constructor(private val context: Context) : Playback,
return false return false
} }
/**
* {@inheritDoc}
*/
override fun onCompletion(mp: MediaPlayer) { override fun onCompletion(mp: MediaPlayer) {
if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) { if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) {
isInitialized = false isInitialized = false
@ -328,34 +317,12 @@ class MultiPlayer internal constructor(private val context: Context) : Playback,
} }
override fun setCrossFadeDuration(duration: Int) {} override fun setCrossFadeDuration(duration: Int) {}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if (key == PLAYBACK_SPEED || key == PLAYBACK_PITCH) {
setPlaybackSpeedPitch(mCurrentMediaPlayer)
}
}
private fun setPlaybackSpeedPitch(mp: MediaPlayer) { override fun setPlaybackSpeedPitch(speed: Float, pitch: Float) {
if (hasMarshmallow()) { mCurrentMediaPlayer.setPlaybackSpeedPitch(speed, pitch)
val wasPlaying = mp.isPlaying
mp.playbackParams = PlaybackParams()
.setSpeed(playbackSpeed)
.setPitch(playbackPitch)
if (!wasPlaying) {
if (mp.isPlaying) mp.pause()
}
}
} }
companion object { companion object {
val TAG: String = MultiPlayer::class.java.simpleName val TAG: String = MultiPlayer::class.java.simpleName
} }
/**
* Constructor of `MultiPlayer`
*/
init {
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
PreferenceManager.getDefaultSharedPreferences(context)
.registerOnSharedPreferenceChangeListener(this)
}
} }

View file

@ -692,15 +692,15 @@ class MusicService : MediaBrowserServiceCompat(),
) { ) {
when (key) { when (key) {
PLAYBACK_SPEED, PLAYBACK_PITCH -> { PLAYBACK_SPEED, PLAYBACK_PITCH -> {
playback?.setPlaybackSpeedPitch(playbackSpeed, playbackPitch) playbackManager.setPlaybackSpeedPitch(playbackSpeed, playbackPitch)
} }
CROSS_FADE_DURATION -> { CROSS_FADE_DURATION -> {
val progress = songProgressMillis val progress = songProgressMillis
val wasPlaying = isPlaying val wasPlaying = isPlaying
playbackManager.maybeSwitchToCrossFade(crossFadeDuration) if (playbackManager.maybeSwitchToCrossFade(crossFadeDuration)) {
restorePlaybackState(wasPlaying, progress) restorePlaybackState(wasPlaying, progress)
}
} }
ALBUM_ART_ON_LOCK_SCREEN, BLURRED_ALBUM_ART -> updateMediaSessionMetaData() ALBUM_ART_ON_LOCK_SCREEN, BLURRED_ALBUM_ART -> updateMediaSessionMetaData()
COLORED_NOTIFICATION -> { COLORED_NOTIFICATION -> {

View file

@ -97,7 +97,6 @@ internal class PlaybackHandler(service: MusicService, looper: Looper) : Handler(
} }
AudioManager.AUDIOFOCUS_LOSS -> { AudioManager.AUDIOFOCUS_LOSS -> {
// Lost focus for an unbounded amount of time: stop playback and release media playback // Lost focus for an unbounded amount of time: stop playback and release media playback
val isAudioFocusEnabled = isAudioFocusEnabled
if (!isAudioFocusEnabled) { if (!isAudioFocusEnabled) {
service.pause(true) service.pause(true)
} }

View file

@ -92,8 +92,12 @@ class PlaybackManager(val context: Context) {
playback?.setCrossFadeDuration(duration) playback?.setCrossFadeDuration(duration)
} }
fun maybeSwitchToCrossFade(crossFadeDuration: Int) { /**
/* Switch to MultiPlayer if Crossfade duration is 0 and * @param crossFadeDuration CrossFade duration
* @return Whether switched playback
*/
fun maybeSwitchToCrossFade(crossFadeDuration: Int): Boolean {
/* Switch to MultiPlayer if CrossFade duration is 0 and
Playback is not an instance of MultiPlayer */ Playback is not an instance of MultiPlayer */
if (playback !is MultiPlayer && crossFadeDuration == 0) { if (playback !is MultiPlayer && crossFadeDuration == 0) {
if (playback != null) { if (playback != null) {
@ -101,13 +105,16 @@ class PlaybackManager(val context: Context) {
} }
playback = null playback = null
playback = MultiPlayer(context) playback = MultiPlayer(context)
return true
} else if (playback !is CrossFadePlayer && crossFadeDuration > 0) { } else if (playback !is CrossFadePlayer && crossFadeDuration > 0) {
if (playback != null) { if (playback != null) {
playback?.release() playback?.release()
} }
playback = null playback = null
playback = CrossFadePlayer(context) playback = CrossFadePlayer(context)
return true
} }
return false
} }
fun release() { fun release() {
@ -126,7 +133,7 @@ class PlaybackManager(val context: Context) {
context.sendBroadcast(intent) context.sendBroadcast(intent)
} }
fun closeAudioEffectSession() { private fun closeAudioEffectSession() {
val audioEffectsIntent = Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION) val audioEffectsIntent = Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)
if (playback != null) { if (playback != null) {
audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION,
@ -141,12 +148,18 @@ class PlaybackManager(val context: Context) {
switchToPlayback(createLocalPlayback(), onChange) switchToPlayback(createLocalPlayback(), onChange)
} }
fun switchToRemotePlayback(castSession: CastSession, onChange: (wasPlaying: Boolean, progress: Int) -> Unit) { fun switchToRemotePlayback(
castSession: CastSession,
onChange: (wasPlaying: Boolean, progress: Int) -> Unit,
) {
playbackLocation = PlaybackLocation.REMOTE playbackLocation = PlaybackLocation.REMOTE
switchToPlayback(CastPlayer(castSession), onChange) switchToPlayback(CastPlayer(castSession), onChange)
} }
private fun switchToPlayback(playback: Playback, onChange: (wasPlaying: Boolean, progress: Int) -> Unit) { private fun switchToPlayback(
playback: Playback,
onChange: (wasPlaying: Boolean, progress: Int) -> Unit,
) {
val oldPlayback = playback val oldPlayback = playback
val wasPlaying: Boolean = oldPlayback.isPlaying val wasPlaying: Boolean = oldPlayback.isPlaying
val progress: Int = oldPlayback.position() val progress: Int = oldPlayback.position()
@ -167,6 +180,9 @@ class PlaybackManager(val context: Context) {
} }
} }
fun setPlaybackSpeedPitch(playbackSpeed: Float, playbackPitch: Float) {
playback?.setPlaybackSpeedPitch(playbackSpeed, playbackPitch)
}
} }
enum class PlaybackLocation { enum class PlaybackLocation {