From e75575228aba42c70a8e2183cf74f4ae45be8ee0 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 7 Jul 2024 00:18:13 +0900 Subject: [PATCH] somewhat working? --- .../musicplayer/gui/MusicPlayerControl.kt | 25 ++--- src/net/torvald/terrarum/MusicService.kt | 24 +++-- .../torvald/terrarum/TerrarumMusicPlaylist.kt | 28 +++--- .../TerrarumMusicAndAmbientStreamer.kt | 92 ++----------------- .../terrarum/transaction/Transaction.kt | 4 +- 5 files changed, 52 insertions(+), 121 deletions(-) diff --git a/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayerControl.kt b/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayerControl.kt index 5a7a4a054..27745b9a9 100644 --- a/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayerControl.kt +++ b/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayerControl.kt @@ -107,12 +107,6 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { //return App.audioMixer.dynamicTracks.any { it.isPlaying && it.trackingTarget is PlaysMusic } }*/ - /** Returns the playlist name from the MusicGovernor. Getting the value from the MusicGovernor - * is recommended as an ingame interaction may cancel the playback from the playlist from the MusicPlayer - * (e.g. interacting with a jukebox) */ - private val internalPlaylistName: String - get() = ingame.musicStreamer.playlistName - private fun registerPlaylist(path: String, fileToName: JsonValue?, shuffled: Boolean, diskJockeyingMode: String): TerrarumMusicPlaylist { fun String.isNum(): Boolean { try { @@ -140,7 +134,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { } } - ingame.musicStreamer.addMusicStartHook { music -> + /*ingame.musicStreamer.addMusicStartHook { music -> setMusicName(music.name) if (mode <= MODE_PLAYING) transitionRequest = MODE_PLAYING @@ -150,7 +144,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { setIntermission() if (mode <= MODE_PLAYING) transitionRequest = MODE_IDLE - } + }*/ setPlaylistDisplayVars(playlist) @@ -369,7 +363,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { } 2 -> { // stop - if (mode < MODE_SHOW_LIST) { // disable stop button entirely on MODE_SHOW_LIST + /*if (mode < MODE_SHOW_LIST) { // disable stop button entirely on MODE_SHOW_LIST // when the button is STOP if (App.audioMixer.musicTrack.isPlaying) { // FIXME the olde way -- must be replaced with one that utilises MusicService @@ -397,11 +391,10 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { stopRequested = false }, /* onFailure: (Throwable) -> Unit */ { - } ) } - } + }*/ } 3 -> { // next @@ -479,7 +472,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { if (index < list.size) { // if selected album is not the same album currently playing, queue that album immediately // (navigating into the selected album involves too much complication :p) - if (ingame.musicStreamer.playlistSource != albumsList[index].canonicalPath) { + if (MusicService.currentPlaylist?.source != albumsList[index].canonicalPath) { // FIXME the olde way -- must be replaced with one that utilises MusicService // fade out /*App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f) { @@ -515,7 +508,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { private var stopRequested = false private fun resetAlbumlistScroll() { - val currentlyPlaying = albumsList.indexOfFirst { it.canonicalPath.replace('\\', '/') == ingame.musicStreamer.playlistSource } + val currentlyPlaying = albumsList.indexOfFirst { it.canonicalPath.replace('\\', '/') == MusicService.currentPlaylist?.source } if (currentlyPlaying >= 0) { albumlistScroll = (currentlyPlaying / PLAYLIST_LINES) * PLAYLIST_LINES } @@ -933,7 +926,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { // print the album name batch.color = Color(1f, 1f, 1f, alpha * 0.75f) - Toolkit.drawTextCentered(batch, App.fontGame, internalPlaylistName, width, x.roundToInt(), 3 + (y + PLAYLIST_LINE_HEIGHT * PLAYLIST_LINES * scale).roundToInt()) + Toolkit.drawTextCentered(batch, App.fontGame, MusicService.currentPlaylist?.name ?: "(null)", width, x.roundToInt(), 3 + (y + PLAYLIST_LINE_HEIGHT * PLAYLIST_LINES * scale).roundToInt()) } } @@ -974,7 +967,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { val pnum = i + albumlistScroll val currentlyPlaying = if (pnum in albumsList.indices) { - val m1 = ingame.musicStreamer.playlistSource + val m1 = MusicService.currentPlaylist?.source val m2 = albumsList[pnum].canonicalPath.replace('\\', '/') (m1 == m2) } @@ -1343,7 +1336,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() { track.doGaplessPlayback = (albumProp.diskJockeyingMode == "continuous") if (track.doGaplessPlayback) { track.pullNextTrack = { - track.currentTrack = MusicService.currentPlaylist!!.peekNext() + track.currentTrack = MusicService.currentPlaylist!!.queueNext() setMusicName(track.currentTrack?.name ?: "") } } diff --git a/src/net/torvald/terrarum/MusicService.kt b/src/net/torvald/terrarum/MusicService.kt index 9ebcec61b..3e4594fec 100644 --- a/src/net/torvald/terrarum/MusicService.kt +++ b/src/net/torvald/terrarum/MusicService.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.audio.AudioBank import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN +import net.torvald.terrarum.audio.audiobank.MusicContainer import net.torvald.terrarum.transaction.Transaction import net.torvald.terrarum.transaction.TransactionListener import net.torvald.terrarum.transaction.TransactionState @@ -69,12 +70,14 @@ object MusicService : TransactionListener() { fun getRandomMusicInterval() = 20f + Math.random().toFloat() * 4f // longer gap (20s to 24s) private fun enterIntermissionAndWaitForPlaylist() { - val time = when (currentPlaylist?.diskJockeyingMode ?: "intermittent") { + val djmode = currentPlaylist?.diskJockeyingMode ?: "intermittent" + val time = when (djmode) { "intermittent" -> getRandomMusicInterval() "continuous" -> 0f else -> getRandomMusicInterval() } enterSTATE_INTERMISSION(time) + if (djmode == "continuous") enterSTATE_FIREPLAY() } fun enterIntermission() { @@ -82,6 +85,7 @@ object MusicService : TransactionListener() { } fun onMusicFinishing(audio: AudioBank) { + printdbg(this, "onMusicFinishing ${audio.name}") enterIntermissionAndWaitForPlaylist() } @@ -96,21 +100,27 @@ object MusicService : TransactionListener() { /* onSuccess: () -> Unit */ { runTransaction(object : Transaction { + private lateinit var nextMusic: MusicContainer + override fun start(state: TransactionState) { - App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getCurrent()) + nextMusic = (state["currentPlaylist"] as TerrarumMusicPlaylist).queueNext() + App.audioMixer.startMusic(nextMusic) } override fun onSuccess(state: TransactionState) { + printdbg(this, "FIREPLAY started music (${nextMusic.name})") enterSTATE_PLAYING() } override fun onFailure(e: Throwable, state: TransactionState) { + printdbg(this, "FIREPLAY resume OK but startMusic failed, entering intermission") enterSTATE_INTERMISSION(getRandomMusicInterval()) // will try again after a random interval } }) }, /* onFailure: (Throwable) -> Unit */ { + printdbg(this, "FIREPLAY resume failed, entering intermission") enterSTATE_INTERMISSION(getRandomMusicInterval()) // will try again after a random interval }, // onFinally: () -> Unit @@ -120,7 +130,7 @@ object MusicService : TransactionListener() { ) } else { - println("PLAYing is deferred: playTransaction is ongoing") + printdbg(this, "FIREPLAY no-op: playTransaction is ongoing") } } STATE_PLAYING -> { @@ -168,11 +178,9 @@ object MusicService : TransactionListener() { * The old playlist will be disposed of if and only if the transaction was successful. * * @param playlist An instance of a [TerrarumMusicPlaylist] to be changed into - * @param onSuccess What to do after the transaction. Default behaviour is: `App.audioMixer.startMusic(playlist.getCurrent())` + * @param onSuccess What to do after the transaction */ - private fun createTransactionPlaylistChange(playlist: TerrarumMusicPlaylist, onSuccess: () -> Unit = { - App.audioMixer.startMusic(playlist.getCurrent()) - }): Transaction { + private fun createTransactionPlaylistChange(playlist: TerrarumMusicPlaylist, onSuccess: () -> Unit): Transaction { return object : Transaction { var oldPlaylist: TerrarumMusicPlaylist? = null @@ -413,7 +421,7 @@ object MusicService : TransactionListener() { if (onSuccess != null) runTransaction(createTransactionPlaylistChange(playlist, onSuccess)) else - runTransaction(createTransactionPlaylistChange(playlist)) + runTransaction(createTransactionPlaylistChange(playlist, {})) } fun putNewPlaylist(playlist: TerrarumMusicPlaylist, onSuccess: () -> Unit, onFinally: () -> Unit) { runTransaction(createTransactionPlaylistChange(playlist, onSuccess), onFinally) diff --git a/src/net/torvald/terrarum/TerrarumMusicPlaylist.kt b/src/net/torvald/terrarum/TerrarumMusicPlaylist.kt index 1a07d7579..16e411724 100644 --- a/src/net/torvald/terrarum/TerrarumMusicPlaylist.kt +++ b/src/net/torvald/terrarum/TerrarumMusicPlaylist.kt @@ -40,7 +40,7 @@ class TerrarumMusicPlaylist( } private fun checkRefill() { - if (internalIndices.size < currentIndexCursor + 1) + if (currentIndexCursor >= internalIndices.size - 2) refillInternalIndices() } @@ -50,19 +50,14 @@ class TerrarumMusicPlaylist( return musicList[internalIndices[currentIndexCursor]] } - fun getNext(): MusicContainer { + fun queueNext(): MusicContainer { checkRefill() currentIndexCursor += 1 return musicList[internalIndices[currentIndexCursor]] } - fun peekNext(): MusicContainer { - checkRefill() - return musicList[internalIndices[currentIndexCursor + 1]] - } - - fun getPrev(): MusicContainer { + fun queuePrev(): MusicContainer { if (currentIndexCursor == 0) { if (shuffled) { musicList.indices.toMutableList().also { if (shuffled) it.shuffle() }.reversed().forEach { @@ -83,12 +78,21 @@ class TerrarumMusicPlaylist( return musicList[internalIndices[currentIndexCursor]] } - - private fun refillInternalIndices() { - internalIndices.addAll(musicList.indices.toMutableList().also { if (shuffled) it.shuffle() }) + fun queueNthSong(n: Int): MusicContainer { + checkRefill() + internalIndices.add(currentIndexCursor, n) + return musicList[internalIndices[currentIndexCursor]] + } + + private fun refillInternalIndices() { + if (diskJockeyingMode == "continuous") { + internalIndices.add(0) // playlist is a one long track for the gapless playback + } + else { + internalIndices.addAll(musicList.indices.toMutableList().also { if (shuffled) it.shuffle() }) + } } - inline fun getNthSong(n: Int) = musicList[n] override fun dispose() { musicList.forEach { diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumMusicAndAmbientStreamer.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumMusicAndAmbientStreamer.kt index 3fca7018c..5b40e6d20 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumMusicAndAmbientStreamer.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumMusicAndAmbientStreamer.kt @@ -9,82 +9,14 @@ import net.torvald.terrarum.audio.audiobank.MusicContainer import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH class TerrarumMusicAndAmbientStreamer : MusicStreamer() { - private val STATE_INIT = 0 + private val STATE_INTERMISSION = 0 private val STATE_FIREPLAY = 1 private val STATE_PLAYING = 2 - private val STATE_INTERMISSION = 3 init { } - private var playlist: List = emptyList() - var playlistName = ""; private set - /** canonicalPath with path separators converted to forward slash */ - var playlistSource = "" ; private set - private var musicBin: ArrayList = ArrayList() - private var shuffled = true - private var diskJockeyingMode = "intermittent" // intermittent, continuous - - - - private fun restockMusicBin() { - musicBin = ArrayList(if (shuffled) playlist.shuffled() else playlist.slice(playlist.indices)) - } - - /** - * Adds a song to the head of the internal playlist (`musicBin`) - */ - fun xxxqueueMusicToPlayNext(music: MusicContainer) { - musicBin.add(0, music) - } - - /** - * Unshifts an internal playlist (`musicBin`). The `music` argument must be the song that exists on the `songs`. - */ - fun xxxunshiftPlaylist(music: MusicContainer) { - val indexAtMusicBin = playlist.indexOf(music) - if (indexAtMusicBin < 0) throw IllegalArgumentException("The music does not exist on the internal songs list ($music)") - - // rewrite musicBin - val newMusicBin = if (shuffled) playlist.shuffled().toTypedArray().also { - // if shuffled, - // 1. create a shuffled version of songlist - // 2. swap two songs such that the songs[indexAtMusicBin] comes first - val swapTo = it.indexOf(playlist[indexAtMusicBin]) - val tmp = it[swapTo] - it[swapTo] = it[0] - it[0] = tmp - } - else Array(playlist.size - indexAtMusicBin) { offset -> - val k = offset + indexAtMusicBin - playlist[k] - } - - musicBin = ArrayList(newMusicBin.toList()) - } - - fun xxxqueueIndexFromPlaylist(indexAtMusicBin: Int) { - if (indexAtMusicBin !in playlist.indices) throw IndexOutOfBoundsException("The index is outside of the internal songs list ($indexAtMusicBin/${playlist.size})") - - // rewrite musicBin - val newMusicBin = if (shuffled) playlist.shuffled().toTypedArray().also { - // if shuffled, - // 1. create a shuffled version of songlist - // 2. swap two songs such that the songs[indexAtMusicBin] comes first - val swapTo = it.indexOf(playlist[indexAtMusicBin]) - val tmp = it[swapTo] - it[swapTo] = it[0] - it[0] = tmp - } - else Array(playlist.size - indexAtMusicBin) { offset -> - val k = offset + indexAtMusicBin - playlist[k] - } - - musicBin = ArrayList(newMusicBin.toList()) - } - private val ambients: HashMap> = HashMap(Terrarum.audioCodex.audio.filter { it.key.startsWith("ambient.") }.map { it.key to it.value.mapNotNull { fileHandle -> try { @@ -100,8 +32,8 @@ class TerrarumMusicAndAmbientStreamer : MusicStreamer() { } }.toHashSet() }.toMap()) - private val musicStartHooks = ArrayList<(MusicContainer) -> Unit>() - private val musicStopHooks = ArrayList<(MusicContainer) -> Unit>() +// private val musicStartHooks = ArrayList<(MusicContainer) -> Unit>() +// private val musicStopHooks = ArrayList<(MusicContainer) -> Unit>() init { // TODO queue and play the default playlist @@ -109,18 +41,15 @@ class TerrarumMusicAndAmbientStreamer : MusicStreamer() { } - fun addMusicStartHook(f: (MusicContainer) -> Unit) { + /*fun addMusicStartHook(f: (MusicContainer) -> Unit) { musicStartHooks.add(f) } fun addMusicStopHook(f: (MusicContainer) -> Unit) { musicStopHooks.add(f) - } + }*/ init { - playlist.forEach { - App.disposables.add(it) - } ambients.forEach { (k, v) -> printdbg(this, "Ambients: $k -> $v") @@ -131,9 +60,6 @@ class TerrarumMusicAndAmbientStreamer : MusicStreamer() { } - private var warningPrinted = false - - protected var ambState = 0 protected var ambFired = false @@ -143,15 +69,15 @@ class TerrarumMusicAndAmbientStreamer : MusicStreamer() { // call MusicService to fade out // pauses playlist update // called by MusicPlayerControl - fun stopMusicPlayback() { + /*fun stopMusicPlayback() { - } + }*/ // resumes playlist update // called by MusicPlayerControl - fun resumeMusicPlayback() { + /*fun resumeMusicPlayback() { - } + }*/ private fun stopAmbient() { // if (::currentAmbientTrack.isInitialized) diff --git a/src/net/torvald/terrarum/transaction/Transaction.kt b/src/net/torvald/terrarum/transaction/Transaction.kt index aac59c09e..c8dd659ab 100644 --- a/src/net/torvald/terrarum/transaction/Transaction.kt +++ b/src/net/torvald/terrarum/transaction/Transaction.kt @@ -76,8 +76,8 @@ abstract class TransactionListener { protected abstract fun commitTransaction(state: TransactionState) } -class LockedException(listener: TransactionListener, lockedBy: Transaction?) : - Exception("Transaction is rejected because the class '${listener.javaClass.canonicalName}' is locked by '${lockedBy?.javaClass?.canonicalName}'") +class LockedException(listener: TransactionListener, lockedBy: Transaction) : + Exception("Transaction is rejected because the class '${listener.javaClass.canonicalName}' is locked by '${lockedBy.javaClass.canonicalName}'") @JvmInline value class TransactionState(val valueTable: HashMap) { operator fun get(key: String) = valueTable[key]