playback: reformat
This commit is contained in:
parent
f245e33887
commit
2c87aa5830
5 changed files with 146 additions and 34 deletions
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,4 +217,4 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, privat
|
|||
exoPlayer.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, _, _ ->
|
||||
|
@ -71,9 +108,10 @@ class PlayerKernelFactoryImpl(@Inject private val mediaSourceFactory: MediaSourc
|
|||
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
||||
.build(),
|
||||
true)
|
||||
.build()
|
||||
.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)
|
||||
|
@ -141,4 +185,4 @@ private class PlayerKernelImpl(
|
|||
super.onPlayerError(error)
|
||||
playerListener.onError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -46,4 +75,4 @@ interface Queuer {
|
|||
interface Factory {
|
||||
fun create(exoPlayer: ExoPlayer, listener: Listener): Queuer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue