From 2c87aa583098434d71583295513c1b577fef3419 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 24 Sep 2024 18:41:13 -0600 Subject: [PATCH] playback: reformat --- .../auxio/playback/player/GaplessQueuer.kt | 57 +++++++++++++---- .../auxio/playback/player/PlayerKernel.kt | 64 ++++++++++++++++--- .../auxio/playback/player/PlayerModule.kt | 2 +- .../playback/player/PlayerStateHolder.kt | 24 ++++--- .../oxycblt/auxio/playback/player/Queuer.kt | 33 +++++++++- 5 files changed, 146 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt index 8d5f41d1d..d44b5942b 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt @@ -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 . + */ + 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, startIndex: Int?, shuffled: Boolean) { @@ -102,7 +129,12 @@ class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer, privat exoPlayer.prepare() } - override fun prepareSaved(mediaItems: List, mapping: List, index: Int, shuffled: Boolean) { + override fun prepareSaved( + mediaItems: List, + mapping: List, + 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() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerKernel.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerKernel.kt index 021cb1158..35103ea58 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerKernel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerKernel.kt @@ -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 . + */ + 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) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerModule.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerModule.kt index d20c80bd0..7c736d607 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerModule.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerModule.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt index 4e306107f..63a23a259 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt @@ -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 } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt index 6701eea76..4f34186eb 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt @@ -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 . + */ + 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 + fun computeMapping(): List + fun computeFirstMediaItemIndex(): Int fun prepareNew(mediaItems: List, startIndex: Int?, shuffled: Boolean) + fun prepareSaved(mediaItems: List, mapping: List, index: Int, shuffled: Boolean) + fun discard() fun addTopMediaItems(mediaItems: List) + fun addBottomMediaItems(mediaItems: List) + fun shuffled(shuffled: Boolean) interface Listener { @@ -46,4 +75,4 @@ interface Queuer { interface Factory { fun create(exoPlayer: ExoPlayer, listener: Listener): Queuer } -} \ No newline at end of file +}