Commit 91802216 authored by Robert Stone's avatar Robert Stone
Browse files

Revise shuffle strategy to pre-compute the playback order

Fixes #647
parent 90bd3979
Pipeline #118668 passed with stage
in 3 minutes and 48 seconds
......@@ -55,7 +55,7 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
var currentIndex = -1
private var nextIndex = -1
private var prevIndex = -1
private var previous = Stack<Int>()
private var shuffleOrder = ArrayList<Int>()
var stopAfter = -1
var repeating = PlaybackStateCompat.REPEAT_MODE_NONE
var shuffling = false
......@@ -126,7 +126,7 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
saveMediaList()
savePosition()
mediaList.removeEventListener(this@PlaylistManager)
previous.clear()
shuffleOrder.clear()
videoBackground = false
mediaList.replaceWith(list)
if (!hasMedia()) {
......@@ -225,7 +225,6 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
mediaList.getMedia(currentIndex)?.let { if (it.type == MediaWrapper.TYPE_VIDEO) it.time = player.getCurrentTime() }
val size = mediaList.size()
if (force || repeating != PlaybackStateCompat.REPEAT_MODE_ONE) {
previous.push(currentIndex)
currentIndex = nextIndex
if (size == 0 || currentIndex < 0 || currentIndex >= size) {
Log.w(TAG, "Warning: invalid next index, aborted !")
......@@ -260,7 +259,7 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
}
}
mediaList.removeEventListener(this)
previous.clear()
shuffleOrder.clear()
currentIndex = -1
if (systemExit) player.release()
else player.restart()
......@@ -279,11 +278,10 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
@MainThread
fun previous(force : Boolean) {
mediaList.getMedia(currentIndex)?.let { if (it.type == MediaWrapper.TYPE_VIDEO) it.time = player.getCurrentTime() }
if (hasPrevious() && currentIndex > 0 &&
if (hasPrevious() && prevIndex >= 0 &&
((force || !player.seekable || (player.getCurrentTime() < PREVIOUS_LIMIT_DELAY) || (lastPrevious != -1L && System.currentTimeMillis() - lastPrevious < PREVIOUS_LIMIT_DELAY)))) {
val size = mediaList.size()
currentIndex = prevIndex
if (previous.size > 0) previous.pop()
if (size == 0 || prevIndex < 0 || currentIndex >= size) {
Log.w(TAG, "Warning: invalid previous index, aborted !")
player.stop()
......@@ -299,7 +297,7 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
@MainThread
fun shuffle() {
if (shuffling) previous.clear()
if (shuffling) shuffleOrder.clear()
shuffling = !shuffling
savePosition()
launch { determinePrevAndNextIndices() }
......@@ -564,7 +562,7 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
// If we are in random mode, we completely reset the stored previous track
// as their indices changed.
previous.clear()
shuffleOrder.clear()
addUpdateActor.safeOffer(Unit)
}
......@@ -585,31 +583,35 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
shuffling = shuffling and (size > 2)
if (shuffling) {
if (!previous.isEmpty()) {
prevIndex = previous.peek()
while (!isValidPosition(prevIndex)) {
previous.removeAt(previous.size - 1)
if (previous.isEmpty()) {
prevIndex = -1
break
}
prevIndex = previous.peek()
}
var shuffleIndex = shuffleOrder.indexOf(currentIndex)
// If we've played all songs already in shuffle,
// check if we need to reshuffle (depending on RepeatType).
if (shuffleIndex == -1 || (shuffleIndex + 1 >= size && repeating != PlaybackStateCompat.REPEAT_MODE_NONE)) {
shuffleOrder.clear()
}
// If we've played all songs already in shuffle, then either
// reshuffle or stop (depending on RepeatType).
if (previous.size + 1 == size) {
if (repeating == PlaybackStateCompat.REPEAT_MODE_NONE) {
nextIndex = -1
return
} else {
previous.clear()
if (shuffleOrder.size != size) {
shuffleOrder = ArrayList(size)
// use O(1) BitSet instead of O(n) contains()
val seqEntries = BitSet(size)
if (currentIndex >= 0) {
// the current song is always first in the shuffle order
shuffleIndex = 0
seqEntries.set(currentIndex)
shuffleOrder.add(currentIndex)
}
while (shuffleOrder.size < size) {
val nextInt = random.nextInt(size)
// find a new index not in seqEntries.
if (!seqEntries.get(nextInt)) {
seqEntries.set(nextInt)
shuffleOrder.add(nextInt)
}
}
}
// Find a new index not in previous.
do {
nextIndex = random.nextInt(size)
} while (nextIndex == currentIndex || previous.contains(nextIndex))
// set prevIndex to -1 for the first entry in the list
prevIndex = if (shuffleIndex > 0) shuffleOrder[shuffleIndex - 1] else -1
// set nextIndex to -1 for the last entry in the list
nextIndex = if (shuffleIndex + 1 < size) shuffleOrder[shuffleIndex + 1] else -1
} else {
// normal playback
if (currentIndex > 0) prevIndex = currentIndex - 1
......@@ -623,9 +625,10 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
}
fun previousTotalTime() = if (shuffling) {
mediaList.copy.asSequence()
.filterIndexed { index, _ -> previous.contains(index) }
.map { it.length }
val mediaCopy = mediaList.copy
ArrayList(shuffleOrder).asSequence()
.takeWhile { it != currentIndex }
.map { mediaCopy[it].length }
.sum()
} else {
mediaList.copy.asSequence()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment