mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 10:04:05 +09:00
166 lines
5.5 KiB
Kotlin
166 lines
5.5 KiB
Kotlin
package net.torvald.terrarum
|
|
|
|
import com.badlogic.gdx.utils.Disposable
|
|
import com.badlogic.gdx.utils.GdxRuntimeException
|
|
import net.torvald.terrarum.App.printdbg
|
|
import net.torvald.terrarum.audio.audiobank.MusicContainer
|
|
import java.io.File
|
|
|
|
/**
|
|
* The `musicList` never (read: should not) gets changed, only the `internalIndices` are being changed as
|
|
* the songs are being played.
|
|
*
|
|
* Created by minjaesong on 2024-06-29.
|
|
*/
|
|
class TerrarumMusicPlaylist(
|
|
/** list of files */
|
|
val musicList: List<MusicContainer>,
|
|
/** name of the album/playlist shown in the [net.torvald.terrarum.musicplayer.gui.MusicPlayer] */
|
|
val name: String,
|
|
/** canonicalPath with path separators converted to forward slash */
|
|
val source: String,
|
|
/** "continuous", "intermittent"; not used by the Playlist itself but by the BackgroundMusicPlayer (aka you are the one who make it actually work) */
|
|
val diskJockeyingMode: String,
|
|
/** if set, the `internalIndices` will be shuffled accordingly, and this happens automatically. (aka you don't need to worry about) */
|
|
val shuffled: Boolean
|
|
): Disposable {
|
|
|
|
private val internalIndices = ArrayList<Int>()
|
|
private var currentIndexCursor = musicList.size - 1
|
|
|
|
init {
|
|
reset()
|
|
}
|
|
|
|
fun reset() {
|
|
internalIndices.clear()
|
|
refillInternalIndices()
|
|
refillInternalIndices()
|
|
currentIndexCursor = musicList.size - 1
|
|
}
|
|
|
|
private fun checkRefill() {
|
|
if (currentIndexCursor >= internalIndices.size - 2)
|
|
refillInternalIndices()
|
|
}
|
|
|
|
fun getCurrent(): MusicContainer {
|
|
checkRefill()
|
|
|
|
return musicList[internalIndices[currentIndexCursor]]
|
|
}
|
|
|
|
/**
|
|
* For Gapless playback, this function is called by track's pullNextTrack callback (defined in [MusicService.createTransactionPlaylistChange])
|
|
*
|
|
* For intermittent playback, this function is called by the transaction defined in [MusicService.update]
|
|
*/
|
|
fun queueNext(): MusicContainer {
|
|
checkRefill()
|
|
currentIndexCursor += 1
|
|
|
|
return musicList[internalIndices[currentIndexCursor]]
|
|
}
|
|
|
|
fun queuePrev(): MusicContainer {
|
|
if (currentIndexCursor == 0) {
|
|
if (shuffled) {
|
|
musicList.indices.toMutableList().also { if (shuffled) it.shuffle() }.reversed().forEach {
|
|
internalIndices.add(0, it)
|
|
}
|
|
currentIndexCursor += musicList.size
|
|
}
|
|
else {
|
|
musicList.indices.reversed().forEach {
|
|
internalIndices.add(0, it)
|
|
}
|
|
currentIndexCursor += musicList.size
|
|
}
|
|
}
|
|
|
|
currentIndexCursor -= 1
|
|
|
|
return musicList[internalIndices[currentIndexCursor]]
|
|
}
|
|
|
|
fun queueNthSong(n: Int): MusicContainer {
|
|
if (shuffled) {
|
|
internalIndices.clear()
|
|
refillInternalIndices()
|
|
internalIndices.add(n)
|
|
refillInternalIndices()
|
|
currentIndexCursor = musicList.size - 1
|
|
}
|
|
else {
|
|
internalIndices.clear()
|
|
refillInternalIndices()
|
|
refillInternalIndices()
|
|
currentIndexCursor = musicList.size - 1 + n
|
|
checkRefill()
|
|
}
|
|
|
|
return musicList[internalIndices[currentIndexCursor]]
|
|
}
|
|
|
|
private fun refillInternalIndices() {
|
|
internalIndices.addAll(musicList.indices.toMutableList().also { if (shuffled) it.shuffle() })
|
|
}
|
|
|
|
|
|
override fun dispose() {
|
|
musicList.forEach {
|
|
it.tryDispose()
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private val validMusicExtensions = hashSetOf("mp3", "wav", "ogg")
|
|
|
|
/**
|
|
* Adding songFinishedHook to the songs is a responsibility of the caller.
|
|
*/
|
|
fun fromDirectory(musicDir: String, shuffled: Boolean, diskJockeyingMode: String, fileToName: ((String) -> String) = { name: String ->
|
|
name.substringBeforeLast('.').replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" ")
|
|
}): TerrarumMusicPlaylist {
|
|
val musicDir = musicDir.replace('\\', '/')
|
|
val playlistSource = musicDir
|
|
printdbg(this, "registerSongsFromDir $musicDir")
|
|
|
|
val playlistName = musicDir.substringAfterLast('/')
|
|
|
|
val playlist = File(musicDir).listFiles()?.sortedBy { it.name }?.filter { Companion.validMusicExtensions.contains(it.extension.lowercase()) }?.mapNotNull {
|
|
printdbg(this, "Music: ${it.absolutePath}")
|
|
try {
|
|
MusicContainer(
|
|
fileToName(it.name),
|
|
it
|
|
)/*.also { muscon ->
|
|
|
|
printdbg(this, "MusicTitle: ${muscon.name}")
|
|
|
|
muscon.songFinishedHook = {
|
|
if (App.audioMixer.musicTrack.currentTrack == it) {
|
|
stopMusic(this, true, getRandomMusicInterval())
|
|
}
|
|
}
|
|
}*/
|
|
// adding songFinishedHook must be done by the caller
|
|
}
|
|
catch (e: GdxRuntimeException) {
|
|
e.printStackTrace()
|
|
null
|
|
}
|
|
} ?: emptyList() // TODO test code
|
|
|
|
return TerrarumMusicPlaylist(
|
|
playlist,
|
|
playlistName,
|
|
musicDir,
|
|
diskJockeyingMode,
|
|
shuffled
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
} |