somewhat working?

This commit is contained in:
minjaesong
2024-07-07 00:18:13 +09:00
parent 90f7e82325
commit e75575228a
5 changed files with 52 additions and 121 deletions

View File

@@ -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 ?: "")
}
}

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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<MusicContainer> = emptyList()
var playlistName = ""; private set
/** canonicalPath with path separators converted to forward slash */
var playlistSource = "" ; private set
private var musicBin: ArrayList<MusicContainer> = 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<String, HashSet<MusicContainer>> =
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)

View File

@@ -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<String, Any?>) {
operator fun get(key: String) = valueTable[key]