Skip to content

Commit

Permalink
playback: restructure repeat mode/listeners
Browse files Browse the repository at this point in the history
  • Loading branch information
OxygenCobalt committed Sep 25, 2024
1 parent b784250 commit f245e33
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,39 @@ package org.oxycblt.auxio.playback.player
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Player.RepeatMode
import androidx.media3.exoplayer.ExoPlayer

class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer) : Queuer {
data object Factory : Queuer.Factory {
override fun create(exoPlayer: ExoPlayer) = GaplessQueuer(exoPlayer)
import org.oxycblt.auxio.playback.PlaybackSettings
import javax.inject.Inject

/**
*
*/
class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, private val listener: Queuer.Listener, private val playbackSettings: PlaybackSettings) : Queuer, PlaybackSettings.Listener, Player.Listener {
class Factory @Inject constructor(private val playbackSettings: PlaybackSettings) : Queuer.Factory {
override fun create(exoPlayer: ExoPlayer, listener: Queuer.Listener) = GaplessQueuer(exoPlayer, listener, playbackSettings)
}

override val currentMediaItem: MediaItem? = exoPlayer.currentMediaItem
override val currentMediaItemIndex: Int = exoPlayer.currentMediaItemIndex
override val shuffleModeEnabled: Boolean = exoPlayer.shuffleModeEnabled
@get:RepeatMode
override var repeatMode: Int = exoPlayer.repeatMode
set(value) {
field = value
exoPlayer.repeatMode = value
updatePauseOnRepeat()
}

override fun attach() {
playbackSettings.registerListener(this)
exoPlayer.addListener(this)
}

override fun release() {
playbackSettings.unregisterListener(this)
exoPlayer.removeListener(this)
}

override fun computeHeap(): List<MediaItem> {
return (0 until exoPlayer.mediaItemCount).map { exoPlayer.getMediaItemAt(it) }
Expand Down Expand Up @@ -142,4 +166,22 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer) : Queu
}
}

override fun onPauseOnRepeatChanged() {
super.onPauseOnRepeatChanged()
updatePauseOnRepeat()
}

private fun updatePauseOnRepeat() {
exoPlayer.pauseAtEndOfMediaItems =
exoPlayer.repeatMode == Player.REPEAT_MODE_ONE && playbackSettings.pauseOnRepeat
}

override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)

if (playbackState == Player.STATE_ENDED && exoPlayer.repeatMode == Player.REPEAT_MODE_OFF) {
goto(0)
exoPlayer.pause()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package org.oxycblt.auxio.playback.player
import android.content.Context
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Player.RepeatMode
import androidx.media3.decoder.ffmpeg.FfmpegAudioRenderer
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.RenderersFactory
Expand All @@ -15,31 +15,36 @@ import androidx.media3.exoplayer.source.MediaSource
import org.oxycblt.auxio.playback.replaygain.ReplayGainAudioProcessor
import javax.inject.Inject

interface PlayerFactory {
fun create(context: Context): ThinPlayer

}

interface ThinPlayer {
interface PlayerKernel {
val isPlaying: Boolean
var playWhenReady: Boolean
val currentPosition: Long
@get:RepeatMode var repeatMode: Int
val audioSessionId: Int
var pauseAtEndOfMediaItems: Boolean
val queuer: Queuer

fun attach(listener: Player.Listener)
fun attach()
fun release()

fun play()
fun pause()
fun seekTo(positionMs: Long)

fun intoQueuer(queuerFactory: Queuer.Factory): Queuer
fun replaceQueuer(queuerFactory: Queuer.Factory)

interface Listener {
fun onPlayWhenReadyChanged()
fun onIsPlayingChanged()
fun onPositionDiscontinuity()
fun onError(error: PlaybackException)
}

interface Factory {
fun create(context: Context, playerListener: Listener, queuerFactory: Queuer.Factory, queuerListener: Queuer.Listener): PlayerKernel
}
}

class PlayerFactoryImpl(@Inject private val mediaSourceFactory: MediaSource.Factory, @Inject private val replayGainProcessor: ReplayGainAudioProcessor) : PlayerFactory {
override fun create(context: Context): ThinPlayer {
class PlayerKernelFactoryImpl(@Inject private val mediaSourceFactory: MediaSource.Factory, @Inject private val replayGainProcessor: ReplayGainAudioProcessor) : PlayerKernel.Factory {
override fun create(context: Context, playerListener: PlayerKernel.Listener, queuerFactory: Queuer.Factory, queuerListener: Queuer.Listener): PlayerKernel {
// Since Auxio is a music player, only specify an audio renderer to save
// battery/apk size/cache size
val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ ->
Expand Down Expand Up @@ -68,39 +73,35 @@ class PlayerFactoryImpl(@Inject private val mediaSourceFactory: MediaSource.Fact
true)
.build()

return ThinPlayerImpl(exoPlayer, replayGainProcessor)
return PlayerKernelImpl(exoPlayer, replayGainProcessor, playerListener, queuerListener, queuerFactory)
}
}

private class ThinPlayerImpl(
private class PlayerKernelImpl(
private val exoPlayer: ExoPlayer,
private val replayGainProcessor: ReplayGainAudioProcessor
) : ThinPlayer {
private val replayGainProcessor: ReplayGainAudioProcessor,
private val playerListener: PlayerKernel.Listener,
private val queuerListener: Queuer.Listener,
queuerFactory: Queuer.Factory
) : PlayerKernel, Player.Listener {
override var queuer: Queuer = queuerFactory.create(exoPlayer, queuerListener)
override val isPlaying: Boolean get() = exoPlayer.isPlaying
override var playWhenReady: Boolean
get() = exoPlayer.playWhenReady
set(value) {
exoPlayer.playWhenReady = value
}
override val currentPosition: Long get() = exoPlayer.currentPosition
override var repeatMode: Int
get() = exoPlayer.repeatMode
set(value) {
exoPlayer.repeatMode = value
}
override val audioSessionId: Int get() = exoPlayer.audioSessionId
override var pauseAtEndOfMediaItems: Boolean
get() = exoPlayer.pauseAtEndOfMediaItems
set(value) {
exoPlayer.pauseAtEndOfMediaItems = value
}

override fun attach(listener: Player.Listener) {
exoPlayer.addListener(listener)
override fun attach() {
exoPlayer.addListener(this)
replayGainProcessor.attach()
queuer.attach()
}

override fun release() {
queuer.release()
replayGainProcessor.release()
exoPlayer.release()
}
Expand All @@ -111,5 +112,33 @@ private class ThinPlayerImpl(

override fun seekTo(positionMs: Long) = exoPlayer.seekTo(positionMs)

override fun intoQueuer(queuerFactory: Queuer.Factory) = queuerFactory.create(exoPlayer)
override fun replaceQueuer(queuerFactory: Queuer.Factory) {
queuer.release()
queuer = queuerFactory.create(exoPlayer, queuerListener)
queuer.attach()
}

override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
playerListener.onPlayWhenReadyChanged()
}

override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
playerListener.onIsPlayingChanged()
}

override fun onPositionDiscontinuity(
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
reason: Int
) {
super.onPositionDiscontinuity(oldPosition, newPosition, reason)
playerListener.onPositionDiscontinuity()
}

override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
playerListener.onError(error)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface PlayerModule {
@Binds fun playerFactory(factory: PlayerFactoryImpl): PlayerFactory
@Binds fun playerKernelFactory(factory: PlayerKernelFactoryImpl): PlayerKernel.Factory
}

@Module
Expand Down
Loading

0 comments on commit f245e33

Please sign in to comment.