mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-16 05:24:06 +09:00
y u no call back :(
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user