playback: reformat

This commit is contained in:
Alexander Capehart 2024-09-24 18:41:13 -06:00
parent f245e33887
commit 2c87aa5830
5 changed files with 146 additions and 34 deletions

View file

@ -1,3 +1,21 @@
/*
* Copyright (c) 2024 Auxio Project
* GaplessQueuer.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback.player
import androidx.media3.common.C
@ -5,15 +23,20 @@ import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Player.RepeatMode
import androidx.media3.exoplayer.ExoPlayer
import org.oxycblt.auxio.playback.PlaybackSettings
import javax.inject.Inject
import org.oxycblt.auxio.playback.PlaybackSettings
/**
*
*/
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)
/** */
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
@ -86,9 +109,13 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, privat
override fun goto(mediaItemIndex: Int) = exoPlayer.seekTo(mediaItemIndex, C.TIME_UNSET)
override fun seekToNext() = exoPlayer.seekToNext()
override fun hasNextMediaItem() = exoPlayer.hasNextMediaItem()
override fun seekToPrevious() = exoPlayer.seekToPrevious()
override fun seekToPreviousMediaItem() = exoPlayer.seekToPreviousMediaItem()
override fun hasPreviousMediaItem() = exoPlayer.hasPreviousMediaItem()
override fun prepareNew(mediaItems: List<MediaItem>, startIndex: Int?, shuffled: Boolean) {
@ -102,7 +129,12 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, privat
exoPlayer.prepare()
}
override fun prepareSaved(mediaItems: List<MediaItem>, mapping: List<Int>, index: Int, shuffled: Boolean) {
override fun prepareSaved(
mediaItems: List<MediaItem>,
mapping: List<Int>,
index: Int,
shuffled: Boolean
) {
exoPlayer.setMediaItems(mediaItems)
if (shuffled) {
exoPlayer.shuffleModeEnabled = true
@ -125,7 +157,9 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, privat
C.INDEX_UNSET
} else {
currTimeline.getNextWindowIndex(
exoPlayer.currentMediaItemIndex, Player.REPEAT_MODE_OFF, exoPlayer.shuffleModeEnabled)
exoPlayer.currentMediaItemIndex,
Player.REPEAT_MODE_OFF,
exoPlayer.shuffleModeEnabled)
}
if (nextIndex == C.INDEX_UNSET) {
@ -161,8 +195,7 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, privat
if (exoPlayer.shuffleModeEnabled) {
// Have to manually refresh the shuffle seed and anchor it to the new current songs
exoPlayer.setShuffleOrder(
BetterShuffleOrder(exoPlayer.mediaItemCount, exoPlayer.currentMediaItemIndex)
)
BetterShuffleOrder(exoPlayer.mediaItemCount, exoPlayer.currentMediaItemIndex))
}
}

View file

@ -1,3 +1,21 @@
/*
* Copyright (c) 2024 Auxio Project
* PlayerKernel.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback.player
import android.content.Context
@ -12,8 +30,8 @@ import androidx.media3.exoplayer.audio.AudioCapabilities
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
import androidx.media3.exoplayer.source.MediaSource
import org.oxycblt.auxio.playback.replaygain.ReplayGainAudioProcessor
import javax.inject.Inject
import org.oxycblt.auxio.playback.replaygain.ReplayGainAudioProcessor
interface PlayerKernel {
val isPlaying: Boolean
@ -23,28 +41,47 @@ interface PlayerKernel {
val queuer: Queuer
fun attach()
fun release()
fun play()
fun pause()
fun seekTo(positionMs: Long)
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
fun create(
context: Context,
playerListener: Listener,
queuerFactory: Queuer.Factory,
queuerListener: Queuer.Listener
): PlayerKernel
}
}
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 {
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, _, _ ->
@ -73,7 +110,8 @@ class PlayerKernelFactoryImpl(@Inject private val mediaSourceFactory: MediaSourc
true)
.build()
return PlayerKernelImpl(exoPlayer, replayGainProcessor, playerListener, queuerListener, queuerFactory)
return PlayerKernelImpl(
exoPlayer, replayGainProcessor, playerListener, queuerListener, queuerFactory)
}
}
@ -85,14 +123,20 @@ private class PlayerKernelImpl(
queuerFactory: Queuer.Factory
) : PlayerKernel, Player.Listener {
override var queuer: Queuer = queuerFactory.create(exoPlayer, queuerListener)
override val isPlaying: Boolean get() = exoPlayer.isPlaying
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 val audioSessionId: Int get() = exoPlayer.audioSessionId
override val currentPosition: Long
get() = exoPlayer.currentPosition
override val audioSessionId: Int
get() = exoPlayer.audioSessionId
override fun attach() {
exoPlayer.addListener(this)

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2023 Auxio Project
* SystemModule.kt is part of Auxio.
* PlayerModule.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2024 Auxio Project
* ExoPlaybackStateHolder.kt is part of Auxio.
* PlayerStateHolder.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,7 +24,6 @@ import android.media.audiofx.AudioEffect
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -55,12 +54,12 @@ import org.oxycblt.auxio.util.logE
class PlayerStateHolder(
private val context: Context,
playerKernelFactory: PlayerKernel.Factory,
gaplessQueuerFactory: Queuer.Factory,
private val playbackManager: PlaybackStateManager,
private val persistenceRepository: PersistenceRepository,
private val playbackSettings: PlaybackSettings,
private val commandFactory: PlaybackCommand.Factory,
private val replayGainProcessor: ReplayGainAudioProcessor,
gaplessQueuerFactory: Queuer.Factory,
private val musicRepository: MusicRepository,
private val imageSettings: ImageSettings
) :
@ -76,9 +75,9 @@ class PlayerStateHolder(
private val persistenceRepository: PersistenceRepository,
private val playbackSettings: PlaybackSettings,
private val playerFactory: PlayerKernel.Factory,
private val gaplessQueuerFactory: Queuer.Factory,
private val commandFactory: PlaybackCommand.Factory,
private val replayGainProcessor: ReplayGainAudioProcessor,
private val gaplessQueuerFactory: Queuer.Factory,
private val musicRepository: MusicRepository,
private val imageSettings: ImageSettings,
) {
@ -86,12 +85,12 @@ class PlayerStateHolder(
return PlayerStateHolder(
context,
playerFactory,
gaplessQueuerFactory,
playbackManager,
persistenceRepository,
playbackSettings,
commandFactory,
replayGainProcessor,
gaplessQueuerFactory,
musicRepository,
imageSettings)
}
@ -149,8 +148,10 @@ class PlayerStateHolder(
override fun resolveQueue(): RawQueue {
val heap = player.queuer.computeHeap()
val shuffledMapping = if (player.queuer.shuffleModeEnabled) player.queuer.computeMapping() else emptyList()
return RawQueue(heap.mapNotNull { it.song }, shuffledMapping, player.queuer.currentMediaItemIndex)
val shuffledMapping =
if (player.queuer.shuffleModeEnabled) player.queuer.computeMapping() else emptyList()
return RawQueue(
heap.mapNotNull { it.song }, shuffledMapping, player.queuer.currentMediaItemIndex)
}
override fun handleDeferred(action: DeferredPlayback): Boolean {
@ -246,7 +247,8 @@ class PlayerStateHolder(
// Replicate the old pseudo-circular queue behavior when no repeat option is implemented.
// Basically, you can't skip back and wrap around the queue, but you can skip forward and
// wrap around the queue, albeit playback will be paused.
if (player.queuer.repeatMode == Player.REPEAT_MODE_ALL || player.queuer.hasNextMediaItem()) {
if (player.queuer.repeatMode == Player.REPEAT_MODE_ALL ||
player.queuer.hasNextMediaItem()) {
player.queuer.seekToNext()
if (!playbackSettings.rememberPause) {
player.play()
@ -346,7 +348,11 @@ class PlayerStateHolder(
sendEvent = true
}
if (rawQueue != resolveQueue()) {
player.queuer.prepareSaved(rawQueue.heap.map { it.buildMediaItem() }, rawQueue.shuffledMapping, rawQueue.heapIndex, rawQueue.isShuffled)
player.queuer.prepareSaved(
rawQueue.heap.map { it.buildMediaItem() },
rawQueue.shuffledMapping,
rawQueue.heapIndex,
rawQueue.isShuffled)
player.pause()
sendEvent = true
}

View file

@ -1,7 +1,24 @@
/*
* Copyright (c) 2024 Auxio Project
* Queuer.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback.player
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Player.RepeatMode
import androidx.media3.exoplayer.ExoPlayer
@ -13,30 +30,42 @@ interface Queuer {
@get:RepeatMode var repeatMode: Int
fun attach()
fun release()
fun goto(mediaItemIndex: Int)
fun seekToNext()
fun hasNextMediaItem(): Boolean
fun seekToPrevious()
fun seekToPreviousMediaItem()
fun hasPreviousMediaItem(): Boolean
fun moveMediaItem(fromIndex: Int, toIndex: Int)
fun removeMediaItem(index: Int)
// EXTENSIONS
fun computeHeap(): List<MediaItem>
fun computeMapping(): List<Int>
fun computeFirstMediaItemIndex(): Int
fun prepareNew(mediaItems: List<MediaItem>, startIndex: Int?, shuffled: Boolean)
fun prepareSaved(mediaItems: List<MediaItem>, mapping: List<Int>, index: Int, shuffled: Boolean)
fun discard()
fun addTopMediaItems(mediaItems: List<MediaItem>)
fun addBottomMediaItems(mediaItems: List<MediaItem>)
fun shuffled(shuffled: Boolean)
interface Listener {