y u no call back :(

This commit is contained in:
minjaesong
2024-07-06 02:04:27 +09:00
parent 68ed16aa5a
commit ff433703f4
10 changed files with 380 additions and 242 deletions

View File

@@ -1,9 +1,12 @@
package net.torvald.terrarum
import net.torvald.terrarum.audio.AudioBank
import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN
import net.torvald.terrarum.transaction.Transaction
import net.torvald.terrarum.transaction.TransactionListener
import net.torvald.terrarum.transaction.TransactionState
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
/**
* To play the music, create a transaction then pass it to the `runTransaction(Transaction)`
@@ -12,18 +15,147 @@ import net.torvald.terrarum.transaction.TransactionState
*/
object MusicService : TransactionListener() {
var currentPlaylist: TerrarumMusicPlaylist? = null; private set
private val currentPlaylistReference = AtomicReference<TerrarumMusicPlaylist?>(null)
val currentPlaylist: TerrarumMusicPlaylist?; get() = currentPlaylistReference.get()
override fun getCurrentStatusForTransaction(): TransactionState {
return TransactionState(
mutableMapOf(
"currentPlaylist" to currentPlaylist
hashMapOf(
"currentPlaylist" to currentPlaylistReference.get()
)
)
}
override fun commitTransaction(state: TransactionState) {
this.currentPlaylist = state["currentPlaylist"] as TerrarumMusicPlaylist?
(state["currentPlaylist"] as TerrarumMusicPlaylist?).let {
this.currentPlaylistReference.set(it)
}
}
private const val STATE_INTERMISSION = 0
private const val STATE_FIREPLAY = 1
private const val STATE_PLAYING = 2
val currentPlaybackState = AtomicInteger(STATE_INTERMISSION)
private var waitAkku = 0f
private var waitTime = 10f
private fun enterSTATE_INTERMISSION(waitFor: Float) {
currentPlaybackState.set(STATE_INTERMISSION)
waitTime = waitFor
waitAkku = 0f
}
private fun enterSTATE_FIREPLAY() {
val state = currentPlaybackState.get()
if (state == STATE_FIREPLAY) throw IllegalStateException("Cannot change state FIREPLAY -> FIREPLAY")
if (state == STATE_PLAYING) throw IllegalStateException("Cannot change state PLAYING -> FIREPLAY")
waitAkku = 0f
currentPlaybackState.set(STATE_FIREPLAY)
}
private fun enterSTATE_PLAYING() {
val state = currentPlaybackState.get()
if (state == STATE_INTERMISSION) throw IllegalStateException("Cannot change state INTERMISSION -> PLAYING")
if (state == STATE_PLAYING) throw IllegalStateException("Cannot change state PLAYING -> PLAYING")
currentPlaybackState.set(STATE_PLAYING)
}
fun getRandomMusicInterval() = 20f + Math.random().toFloat() * 4f // longer gap (20s to 24s)
private fun enterIntermissionAndWaitForPlaylist() {
val time = when (currentPlaylist?.diskJockeyingMode ?: "intermittent") {
"intermittent" -> getRandomMusicInterval()
"continuous" -> 0f
else -> getRandomMusicInterval()
}
enterSTATE_INTERMISSION(time)
}
fun enterIntermission() {
enterSTATE_INTERMISSION(getRandomMusicInterval())
}
fun onMusicFinishing(audio: AudioBank) {
synchronized(this) {
enterIntermissionAndWaitForPlaylist()
}
}
private var playTransactionOngoing = false
fun update(delta: Float) {
when (currentPlaybackState.get()) {
STATE_FIREPLAY -> {
if (!playTransactionOngoing) {
playTransactionOngoing = true
MusicService.resumePlaylistPlayback(
/* onSuccess: () -> Unit */
{
runTransaction(object : Transaction {
override fun start(state: TransactionState) {
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getCurrent())
}
override fun onSuccess(state: TransactionState) {
enterSTATE_PLAYING()
}
override fun onFailure(e: Throwable, state: TransactionState) {
enterSTATE_INTERMISSION(getRandomMusicInterval()) // will try again after a random interval
}
})
},
/* onFailure: (Throwable) -> Unit */
{
enterSTATE_INTERMISSION(getRandomMusicInterval()) // will try again after a random interval
},
// onFinally: () -> Unit
{
playTransactionOngoing = false
}
)
}
}
STATE_PLAYING -> {
// onMusicFinishing() will be called when the music finishes; it's on the setOnCompletionListener
}
STATE_INTERMISSION -> {
waitAkku += delta
if (waitAkku >= waitTime && currentPlaylist != null) {
enterSTATE_FIREPLAY()
}
}
}
}
fun enterScene(id: String) {
synchronized(this) {
/*val playlist = when (id) {
"title" -> getTitlePlaylist()
"ingame" -> getIngameDefaultPlaylist()
else -> getIngameDefaultPlaylist()
}
putNewPlaylist(playlist) {
// after the fadeout, we'll...
enterSTATE_FIREPLAY()
}*/
stopPlaylistPlayback { }
}
}
fun leaveScene() {
synchronized(this) {
stopPlaylistPlayback {}
}
}
/**
@@ -32,6 +164,9 @@ object MusicService : TransactionListener() {
* be reset as soon as the transaction starts. Note that the resetting behaviour is NOT atomic. (the given
* playlist will stay in reset state even if the transaction fails)
*
* When the transaction was successful, the old playlist gets disposed of, then the songFinishedHook of
* the songs in the new playlist will be overwritten, before `onSuccess` is called.
*
* 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
@@ -45,7 +180,6 @@ object MusicService : TransactionListener() {
override fun start(state: TransactionState) {
oldPlaylist = state["currentPlaylist"] as TerrarumMusicPlaylist?
if (oldPlaylist == playlist) return
playlist.reset()
@@ -64,15 +198,27 @@ object MusicService : TransactionListener() {
waitUntil { fadedOut }
}
else {
/* do nothing */
// put new playlist
state["currentPlaylist"] = playlist
}
}
override fun onSuccess(state: TransactionState) {
oldPlaylist?.dispose()
(state["currentPlaylist"] as TerrarumMusicPlaylist?)?.let {
it.musicList.forEach {
it.songFinishedHook = {
onMusicFinishing(it)
}
}
}
onSuccess()
}
override fun onFailure(e: Throwable, state: TransactionState) {}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
}
}
}
@@ -80,20 +226,30 @@ object MusicService : TransactionListener() {
return object : Transaction {
override fun start(state: TransactionState) {
var fadedOut = false
var err: Throwable? = null
// request fadeout
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
// callback: play next song in the playlist
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getNext())
fadedOut = true
try {
// callback: play next song in the playlist
// TODO queue the next song on the playlist, the actual playback will be done by the state machine update
fadedOut = true
}
catch (e: Throwable) {
err = e
}
}
waitUntil { fadedOut }
waitUntil { fadedOut || err != null }
if (err != null) throw err!!
}
override fun onSuccess(state: TransactionState) {
onSuccess()
}
override fun onFailure(e: Throwable, state: TransactionState) {}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
}
}
}
@@ -101,20 +257,30 @@ object MusicService : TransactionListener() {
return object : Transaction {
override fun start(state: TransactionState) {
var fadedOut = false
var err: Throwable? = null
// request fadeout
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
// callback: play prev song in the playlist
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getPrev())
fadedOut = true
try {
// callback: play prev song in the playlist
// TODO queue the prev song on the playlist, the actual playback will be done by the state machine update
fadedOut = true
}
catch (e: Throwable) {
err = e
}
}
waitUntil { fadedOut }
waitUntil { fadedOut || err != null }
if (err != null) throw err!!
}
override fun onSuccess(state: TransactionState) {
onSuccess()
}
override fun onFailure(e: Throwable, state: TransactionState) {}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
}
}
}
@@ -122,18 +288,29 @@ object MusicService : TransactionListener() {
return object : Transaction {
override fun start(state: TransactionState) {
var fadedOut = false
var err: Throwable? = null
// request fadeout
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
// callback: play prev song in the playlist
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getNthSong(index))
fadedOut = true
try {
// callback: play prev song in the playlist
// TODO queue the nth song on the playlist, the actual playback will be done by the state machine update
fadedOut = true
}
catch (e: Throwable) {
err = e
}
}
waitUntil { fadedOut }
waitUntil { fadedOut || err != null }
if (err != null) throw err!!
}
override fun onSuccess(state: TransactionState) { onSuccess() }
override fun onFailure(e: Throwable, state: TransactionState) {}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
}
}
}
@@ -147,21 +324,32 @@ object MusicService : TransactionListener() {
}
waitUntil { fadedOut }
enterSTATE_INTERMISSION(Float.POSITIVE_INFINITY)
}
override fun onSuccess(state: TransactionState) { onSuccess() }
override fun onFailure(e: Throwable, state: TransactionState) {}
override fun onSuccess(state: TransactionState) {
onSuccess()
}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
}
}
}
private fun createTransactionForPlaylistResume(onSuccess: () -> Unit): Transaction {
private fun createTransactionForPlaylistResume(onSuccess: () -> Unit, onFailure: (Throwable) -> Unit): Transaction {
return object : Transaction {
override fun start(state: TransactionState) {
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getCurrent())
enterSTATE_FIREPLAY()
}
override fun onSuccess(state: TransactionState) { onSuccess() }
override fun onFailure(e: Throwable, state: TransactionState) {}
override fun onSuccess(state: TransactionState) {
onSuccess()
}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
onFailure(e)
}
}
}
@@ -174,22 +362,44 @@ object MusicService : TransactionListener() {
return object : Transaction {
override fun start(state: TransactionState) {
var fadedOut = false
var err: Throwable? = null
println("createTransactionPausePlaylistForMusicalFixture start")
// request fadeout
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2.0) {
// callback: let the caller actually take care of playing the audio
action()
println("createTransactionPausePlaylistForMusicalFixture fadeout end")
try {
// callback: let the caller actually take care of playing the audio
action()
fadedOut = true
fadedOut = true
}
catch (e: Throwable) {
err = e
e.printStackTrace()
}
}
waitUntil { fadedOut }
waitUntil { fadedOut || err != null }
if (err != null) throw err!!
// enter intermission state
println("createTransactionPausePlaylistForMusicalFixture fadeout waiting end, entering INTERMISSION state")
enterSTATE_INTERMISSION(Float.POSITIVE_INFINITY)
// wait until the interjected music finishes
println("createTransactionPausePlaylistForMusicalFixture waiting for musicFinished()")
waitUntil { musicFinished() }
}
override fun onSuccess(state: TransactionState) { onSuccess() }
override fun onFailure(e: Throwable, state: TransactionState) { onFailure(e) }
override fun onSuccess(state: TransactionState) {
onSuccess()
enterSTATE_INTERMISSION(getRandomMusicInterval())
}
override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace()
onFailure(e)
enterSTATE_INTERMISSION(getRandomMusicInterval())
}
}
// note to self: wait() and notify() using a lock object is impractical as the Java thread can wake up
@@ -215,9 +425,11 @@ object MusicService : TransactionListener() {
runTransaction(createTransactionPlaylistChange(playlist, onSuccess), onFinally)
}
/** Normal playlist playback will resume after the transaction, after the onSuccess/onFailure */
fun playMusicalFixture(action: () -> Unit, musicFinished: () -> Boolean, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) {
runTransaction(createTransactionPausePlaylistForMusicalFixture(action, musicFinished, onSuccess, onFailure))
}
/** Normal playlist playback will resume after the transaction, after the onSuccess/onFailure but before the onFinally */
fun playMusicalFixture(action: () -> Unit, musicFinished: () -> Boolean, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit, onFinally: () -> Unit = {}) {
runTransaction(createTransactionPausePlaylistForMusicalFixture(action, musicFinished, onSuccess, onFailure), onFinally)
}
@@ -250,10 +462,10 @@ object MusicService : TransactionListener() {
runTransaction(createTransactionForPlaylistStop(onSuccess), onFinally)
}
fun resumePlaylistPlayback(onSuccess: () -> Unit) {
runTransaction(createTransactionForPlaylistResume(onSuccess))
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) {
runTransaction(createTransactionForPlaylistResume(onSuccess, onFailure))
}
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFinally: () -> Unit) {
runTransaction(createTransactionForPlaylistResume(onSuccess), onFinally)
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFailure: (Throwable) -> Unit, onFinally: () -> Unit) {
runTransaction(createTransactionForPlaylistResume(onSuccess, onFailure), onFinally)
}
}