mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
transaction-based music playback managing wip
This commit is contained in:
8
.idea/kotlinc.xml
generated
8
.idea/kotlinc.xml
generated
@@ -1,13 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Kotlin2JvmCompilerArguments">
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
<option name="jvmTarget" value="17" />
|
<option name="jvmTarget" value="21" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinCommonCompilerArguments">
|
<component name="KotlinCommonCompilerArguments">
|
||||||
<option name="apiVersion" value="1.8" />
|
<option name="apiVersion" value="1.9" />
|
||||||
<option name="languageVersion" value="1.8" />
|
<option name="languageVersion" value="1.9" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.8.0" />
|
<option name="version" value="1.9.23-release-779" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
package net.torvald.terrarum.musicplayer
|
package net.torvald.terrarum.musicplayer
|
||||||
|
|
||||||
import net.torvald.terrarum.IngameInstance
|
|
||||||
import net.torvald.terrarum.ModMgr
|
import net.torvald.terrarum.ModMgr
|
||||||
import net.torvald.terrarum.ModuleEntryPoint
|
import net.torvald.terrarum.ModuleEntryPoint
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
import net.torvald.terrarum.musicplayer.gui.MusicPlayer
|
import net.torvald.terrarum.musicplayer.gui.MusicPlayerControl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2023-12-23.
|
* Created by minjaesong on 2023-12-23.
|
||||||
*/
|
*/
|
||||||
class EntryPoint : ModuleEntryPoint() {
|
class EntryPoint : ModuleEntryPoint() {
|
||||||
override fun invoke() {
|
override fun invoke() {
|
||||||
ModMgr.GameExtraGuiLoader.register { ingame: TerrarumIngame -> MusicPlayer(ingame) }
|
ModMgr.GameExtraGuiLoader.register { ingame: TerrarumIngame -> MusicPlayerControl(ingame) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import com.badlogic.gdx.utils.JsonValue
|
|||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
import net.torvald.reflection.extortField
|
import net.torvald.reflection.extortField
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.App.printdbg
|
|
||||||
import net.torvald.terrarum.audio.*
|
import net.torvald.terrarum.audio.*
|
||||||
import net.torvald.terrarum.audio.audiobank.MusicContainer
|
import net.torvald.terrarum.audio.audiobank.MusicContainer
|
||||||
import net.torvald.terrarum.gameworld.fmod
|
import net.torvald.terrarum.gameworld.fmod
|
||||||
@@ -33,7 +32,7 @@ import kotlin.math.*
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2023-12-23.
|
* Created by minjaesong on 2023-12-23.
|
||||||
*/
|
*/
|
||||||
class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||||
|
|
||||||
private val STRIP_W = 9f
|
private val STRIP_W = 9f
|
||||||
private val METERS_WIDTH = 2 * STRIP_W
|
private val METERS_WIDTH = 2 * STRIP_W
|
||||||
@@ -96,20 +95,21 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
|
|
||||||
/** Returns the internal playlist of the MusicGovernor */
|
/** Returns the internal playlist of the MusicGovernor */
|
||||||
private val songsInGovernor: List<MusicContainer>
|
private val songsInGovernor: List<MusicContainer>
|
||||||
get() = ingame.musicGovernor.extortField<List<MusicContainer>>("songs")!!
|
get() = ingame.backgroundMusicPlayer.extortField<List<MusicContainer>>("songs")!!
|
||||||
|
|
||||||
private val shouldPlayerBeDisabled: Boolean
|
/*private val shouldPlayerBeDisabled: Boolean
|
||||||
get() {
|
get() {
|
||||||
return App.audioMixer.dynamicTracks.any { it.isPlaying && it.trackingTarget is PlaysMusic }
|
return MusicService.transactionLocked
|
||||||
}
|
//return App.audioMixer.dynamicTracks.any { it.isPlaying && it.trackingTarget is PlaysMusic }
|
||||||
|
}*/
|
||||||
|
|
||||||
/** Returns the playlist name from the MusicGovernor. Getting the value from the MusicGovernor
|
/** 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
|
* is recommended as an ingame interaction may cancel the playback from the playlist from the MusicPlayer
|
||||||
* (e.g. interacting with a jukebox) */
|
* (e.g. interacting with a jukebox) */
|
||||||
private val internalPlaylistName: String
|
private val internalPlaylistName: String
|
||||||
get() = ingame.musicGovernor.playlistName
|
get() = ingame.backgroundMusicPlayer.playlistName
|
||||||
|
|
||||||
fun registerPlaylist(path: String, fileToName: JsonValue?, shuffled: Boolean, diskJockeyingMode: String) {
|
fun registerPlaylist(path: String, fileToName: JsonValue?, shuffled: Boolean, diskJockeyingMode: String): TerrarumMusicPlaylist {
|
||||||
fun String.isNum(): Boolean {
|
fun String.isNum(): Boolean {
|
||||||
try {
|
try {
|
||||||
this.toInt()
|
this.toInt()
|
||||||
@@ -120,7 +120,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ingame.musicGovernor.queueDirectory(path, shuffled, diskJockeyingMode) { filename ->
|
val playlist = ingame.backgroundMusicPlayer.queueDirectory(path, shuffled, diskJockeyingMode, false) { filename ->
|
||||||
fileToName?.get(filename).let {
|
fileToName?.get(filename).let {
|
||||||
if (it == null)
|
if (it == null)
|
||||||
filename.substringBeforeLast('.').replace('_', ' ').split(" ").map { it.capitalize() }.let {
|
filename.substringBeforeLast('.').replace('_', ' ').split(" ").map { it.capitalize() }.let {
|
||||||
@@ -136,19 +136,21 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ingame.musicGovernor.addMusicStartHook { music ->
|
ingame.backgroundMusicPlayer.addMusicStartHook { music ->
|
||||||
setMusicName(music.name)
|
setMusicName(music.name)
|
||||||
if (mode <= MODE_PLAYING)
|
if (mode <= MODE_PLAYING)
|
||||||
transitionRequest = MODE_PLAYING
|
transitionRequest = MODE_PLAYING
|
||||||
}
|
}
|
||||||
|
|
||||||
ingame.musicGovernor.addMusicStopHook { music ->
|
ingame.backgroundMusicPlayer.addMusicStopHook { music ->
|
||||||
setIntermission()
|
setIntermission()
|
||||||
if (mode <= MODE_PLAYING)
|
if (mode <= MODE_PLAYING)
|
||||||
transitionRequest = MODE_IDLE
|
transitionRequest = MODE_IDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlaylistDisplayVars(songsInGovernor)
|
setPlaylistDisplayVars(songsInGovernor)
|
||||||
|
|
||||||
|
return playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentMusicName = ""
|
private var currentMusicName = ""
|
||||||
@@ -186,7 +188,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
private var mouseOnList: Int? = null
|
private var mouseOnList: Int? = null
|
||||||
|
|
||||||
override fun updateImpl(delta: Float) {
|
override fun updateImpl(delta: Float) {
|
||||||
val shouldPlayerBeDisabled = shouldPlayerBeDisabled
|
val transactionLocked = MusicService.transactionLocked
|
||||||
|
|
||||||
// process transition request
|
// process transition request
|
||||||
if (transitionRequest != null) {
|
if (transitionRequest != null) {
|
||||||
@@ -345,16 +347,10 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
1 -> { // prev
|
1 -> { // prev
|
||||||
// prev song
|
// prev song
|
||||||
if (mode < MODE_SHOW_LIST) {
|
if (mode < MODE_SHOW_LIST) {
|
||||||
getPrevSongFromPlaylist()?.let { ingame.musicGovernor.unshiftPlaylist(it) }
|
MusicService.playPrevSongInPlaylist {
|
||||||
if (!shouldPlayerBeDisabled) {
|
ingame.backgroundMusicPlayer.startMusic(this) // required for "intermittent" mode
|
||||||
App.audioMixer.requestFadeOut(
|
iHitTheStopButton = false
|
||||||
App.audioMixer.musicTrack,
|
stopRequested = false
|
||||||
AudioMixer.DEFAULT_FADEOUT_LEN / 3f
|
|
||||||
) {
|
|
||||||
ingame.musicGovernor.startMusic(this) // required for "intermittent" mode
|
|
||||||
iHitTheStopButton = false
|
|
||||||
stopRequested = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// prev page in the playlist
|
// prev page in the playlist
|
||||||
@@ -372,18 +368,31 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
|
|
||||||
2 -> { // stop
|
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) {
|
if (App.audioMixer.musicTrack.isPlaying) {
|
||||||
val thisMusic = App.audioMixer.musicTrack.currentTrack
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
|
/*val thisMusic = App.audioMixer.musicTrack.currentTrack
|
||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f)
|
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f)
|
||||||
App.audioMixer.musicTrack.nextTrack = null
|
App.audioMixer.musicTrack.nextTrack = null
|
||||||
ingame.musicGovernor.stopMusic(this)
|
ingame.backgroundMusicPlayer.stopMusic(this)
|
||||||
if (thisMusic is MusicContainer) thisMusic.let { ingame.musicGovernor.queueMusicToPlayNext(it) }
|
if (thisMusic is MusicContainer) thisMusic.let { ingame.backgroundMusicPlayer.queueMusicToPlayNext(it) }
|
||||||
iHitTheStopButton = true
|
iHitTheStopButton = true*/
|
||||||
|
|
||||||
|
MusicService.stopPlaylistPlayback {
|
||||||
|
iHitTheStopButton = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!shouldPlayerBeDisabled) {
|
// when the button is PLAY
|
||||||
ingame.musicGovernor.startMusic(this)
|
else if (!App.audioMixer.musicTrack.isPlaying) {
|
||||||
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
|
/*ingame.backgroundMusicPlayer.startMusic(this)
|
||||||
iHitTheStopButton = false
|
iHitTheStopButton = false
|
||||||
stopRequested = false
|
stopRequested = false*/
|
||||||
|
|
||||||
|
MusicService.resumePlaylistPlayback {
|
||||||
|
iHitTheStopButton = false
|
||||||
|
stopRequested = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,15 +400,10 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
3 -> { // next
|
3 -> { // next
|
||||||
// next song
|
// next song
|
||||||
if (mode < MODE_SHOW_LIST) {
|
if (mode < MODE_SHOW_LIST) {
|
||||||
if (!shouldPlayerBeDisabled) {
|
MusicService.playNextSongInPlaylist {
|
||||||
App.audioMixer.requestFadeOut(
|
ingame.backgroundMusicPlayer.startMusic(this) // required for "intermittent" mode
|
||||||
App.audioMixer.musicTrack,
|
iHitTheStopButton = false
|
||||||
AudioMixer.DEFAULT_FADEOUT_LEN / 3f
|
stopRequested = false
|
||||||
) {
|
|
||||||
ingame.musicGovernor.startMusic(this) // required for "intermittent" mode, does seemingly nothing on "continuous" mode
|
|
||||||
iHitTheStopButton = false
|
|
||||||
stopRequested = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// next page in the playlist
|
// next page in the playlist
|
||||||
@@ -435,28 +439,34 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// make playlist clicking work
|
// make playlist clicking (change song within the playlist) work
|
||||||
else if (listViewPanelScroll == 1f && mouseOnList != null) {
|
else if (listViewPanelScroll == 1f && mouseOnList != null) {
|
||||||
val index = playlistScroll + mouseOnList!!
|
val index = playlistScroll + mouseOnList!!
|
||||||
val list = songsInGovernor
|
val list = songsInGovernor
|
||||||
if (index < list.size) {
|
if (index < list.size) {
|
||||||
// if selected song != currently playing
|
// if selected song != currently playing
|
||||||
if (App.audioMixer.musicTrack.currentTrack == null || list[index] != App.audioMixer.musicTrack.currentTrack) {
|
if (App.audioMixer.musicTrack.currentTrack == null || list[index] != App.audioMixer.musicTrack.currentTrack) {
|
||||||
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
// rebuild playlist
|
// rebuild playlist
|
||||||
ingame.musicGovernor.queueIndexFromPlaylist(index)
|
//ingame.backgroundMusicPlayer.queueIndexFromPlaylist(index)
|
||||||
|
|
||||||
// fade out
|
// fade out
|
||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f) {
|
/*App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f) {
|
||||||
if (!shouldPlayerBeDisabled) {
|
if (!shouldPlayerBeDisabled) {
|
||||||
ingame.musicGovernor.startMusic(this) // required for "intermittent" mode
|
ingame.backgroundMusicPlayer.startMusic(this) // required for "intermittent" mode
|
||||||
iHitTheStopButton = false
|
iHitTheStopButton = false
|
||||||
stopRequested = false
|
stopRequested = false
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
MusicService.playNthSongInPlaylist(index) {
|
||||||
|
ingame.backgroundMusicPlayer.startMusic(this) // required for "intermittent" mode
|
||||||
|
iHitTheStopButton = false
|
||||||
|
stopRequested = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// make album list clicking work
|
// make album list clicking (send new playlist to the MusicService) work
|
||||||
else if (listViewPanelScroll == 0f && mouseOnList != null) {
|
else if (listViewPanelScroll == 0f && mouseOnList != null) {
|
||||||
val index = albumlistScroll + mouseOnList!!
|
val index = albumlistScroll + mouseOnList!!
|
||||||
val list = albumsList//.map { albumPropCache[it] }
|
val list = albumsList//.map { albumPropCache[it] }
|
||||||
@@ -464,16 +474,23 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
if (index < list.size) {
|
if (index < list.size) {
|
||||||
// if selected album is not the same album currently playing, queue that album immediately
|
// if selected album is not the same album currently playing, queue that album immediately
|
||||||
// (navigating into the selected album involves too much complication :p)
|
// (navigating into the selected album involves too much complication :p)
|
||||||
if (ingame.musicGovernor.playlistSource != albumsList[index].canonicalPath) {
|
if (ingame.backgroundMusicPlayer.playlistSource != albumsList[index].canonicalPath) {
|
||||||
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
// fade out
|
// fade out
|
||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f) {
|
/*App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f) {
|
||||||
loadNewAlbum(albumsList[index])
|
loadNewAlbum(albumsList[index])
|
||||||
if (!shouldPlayerBeDisabled) {
|
if (!shouldPlayerBeDisabled) {
|
||||||
ingame.musicGovernor.startMusic(this) // required for "intermittent" mode
|
ingame.backgroundMusicPlayer.startMusic(this) // required for "intermittent" mode
|
||||||
iHitTheStopButton = false
|
iHitTheStopButton = false
|
||||||
stopRequested = false
|
stopRequested = false
|
||||||
}
|
}
|
||||||
resetPlaylistScroll(App.audioMixer.musicTrack.nextTrack as? MusicContainer)
|
resetPlaylistScroll(App.audioMixer.musicTrack.nextTrack as? MusicContainer)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
val playlist = loadNewAlbum(albumsList[index])
|
||||||
|
MusicService.putNewPlaylist(playlist) {
|
||||||
|
resetPlaylistScroll(App.audioMixer.musicTrack.nextTrack as? MusicContainer)
|
||||||
|
App.audioMixer.startMusic(playlist.getCurrent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -487,16 +504,16 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
|
|
||||||
// printdbg(this, "mode = $mode; req = $transitionRequest")
|
// printdbg(this, "mode = $mode; req = $transitionRequest")
|
||||||
|
|
||||||
if (shouldPlayerBeDisabled || iHitTheStopButton) {
|
/*if (shouldPlayerBeDisabled || iHitTheStopButton) {
|
||||||
if (!stopRequested) {
|
if (!stopRequested) {
|
||||||
stopRequested = true
|
stopRequested = true
|
||||||
ingame.musicGovernor.stopMusic(this)
|
ingame.backgroundMusicPlayer.stopMusic(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ingame.musicGovernor.playCaller is PlaysMusic && !jukeboxStopMonitorAlert && !App.audioMixer.musicTrack.isPlaying) {
|
else*/ if (ingame.backgroundMusicPlayer.playCaller is PlaysMusic && !jukeboxStopMonitorAlert && !App.audioMixer.musicTrack.isPlaying) {
|
||||||
jukeboxStopMonitorAlert = true
|
jukeboxStopMonitorAlert = true
|
||||||
val interval = ingame.musicGovernor.getRandomMusicInterval()
|
val interval = ingame.backgroundMusicPlayer.getRandomMusicInterval()
|
||||||
ingame.musicGovernor.stopMusic(this, false, interval)
|
ingame.backgroundMusicPlayer.stopMusic(this, false, interval)
|
||||||
}
|
}
|
||||||
else if (App.audioMixer.musicTrack.isPlaying) {
|
else if (App.audioMixer.musicTrack.isPlaying) {
|
||||||
jukeboxStopMonitorAlert = false
|
jukeboxStopMonitorAlert = false
|
||||||
@@ -508,7 +525,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
private var stopRequested = false
|
private var stopRequested = false
|
||||||
|
|
||||||
private fun resetAlbumlistScroll() {
|
private fun resetAlbumlistScroll() {
|
||||||
val currentlyPlaying = albumsList.indexOfFirst { it.canonicalPath.replace('\\', '/') == ingame.musicGovernor.playlistSource }
|
val currentlyPlaying = albumsList.indexOfFirst { it.canonicalPath.replace('\\', '/') == ingame.backgroundMusicPlayer.playlistSource }
|
||||||
if (currentlyPlaying >= 0) {
|
if (currentlyPlaying >= 0) {
|
||||||
albumlistScroll = (currentlyPlaying / PLAYLIST_LINES) * PLAYLIST_LINES
|
albumlistScroll = (currentlyPlaying / PLAYLIST_LINES) * PLAYLIST_LINES
|
||||||
}
|
}
|
||||||
@@ -527,7 +544,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPrevSongFromPlaylist(): MusicContainer? {
|
/*private fun getPrevSongFromPlaylist(): MusicContainer? {
|
||||||
val list = songsInGovernor.slice(songsInGovernor.indices) // make copy of the list
|
val list = songsInGovernor.slice(songsInGovernor.indices) // make copy of the list
|
||||||
val nowPlaying = App.audioMixer.musicTrack.currentTrack ?: return null
|
val nowPlaying = App.audioMixer.musicTrack.currentTrack ?: return null
|
||||||
|
|
||||||
@@ -537,7 +554,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
|
|
||||||
val prevIndex = (currentIndex - 1).fmod(list.size)
|
val prevIndex = (currentIndex - 1).fmod(list.size)
|
||||||
return list[prevIndex]
|
return list[prevIndex]
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// private fun smoothstep(x: Float) = (x*x*(3f-2f*x)).coerceIn(0f, 1f)
|
// private fun smoothstep(x: Float) = (x*x*(3f-2f*x)).coerceIn(0f, 1f)
|
||||||
// private fun smootherstep(x: Float) = (x*x*x*(x*(6f*x-15f)+10f)).coerceIn(0f, 1f)
|
// private fun smootherstep(x: Float) = (x*x*x*(x*(6f*x-15f)+10f)).coerceIn(0f, 1f)
|
||||||
@@ -919,7 +936,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
val pnum = i + albumlistScroll
|
val pnum = i + albumlistScroll
|
||||||
|
|
||||||
val currentlyPlaying = if (pnum in albumsList.indices) {
|
val currentlyPlaying = if (pnum in albumsList.indices) {
|
||||||
val m1 = ingame.musicGovernor.playlistSource
|
val m1 = ingame.backgroundMusicPlayer.playlistSource
|
||||||
val m2 = albumsList[pnum].canonicalPath.replace('\\', '/')
|
val m2 = albumsList[pnum].canonicalPath.replace('\\', '/')
|
||||||
(m1 == m2)
|
(m1 == m2)
|
||||||
}
|
}
|
||||||
@@ -1279,14 +1296,14 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
val albumArt: TextureRegion? = null
|
val albumArt: TextureRegion? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun loadNewAlbum(albumDir: File) {
|
private fun loadNewAlbum(albumDir: File): TerrarumMusicPlaylist {
|
||||||
val albumProp = albumPropCache[albumDir]
|
val albumProp = albumPropCache[albumDir]
|
||||||
|
|
||||||
App.audioMixer.musicTrack.let { track ->
|
App.audioMixer.musicTrack.let { track ->
|
||||||
track.doGaplessPlayback = (albumProp.diskJockeyingMode == "continuous")
|
track.doGaplessPlayback = (albumProp.diskJockeyingMode == "continuous")
|
||||||
if (track.doGaplessPlayback) {
|
if (track.doGaplessPlayback) {
|
||||||
track.pullNextTrack = {
|
track.pullNextTrack = {
|
||||||
track.currentTrack = ingame.musicGovernor.pullNextMusicTrack(true)
|
track.currentTrack = ingame.backgroundMusicPlayer.pullNextMusicTrack(true)
|
||||||
setMusicName(track.currentTrack?.name ?: "")
|
setMusicName(track.currentTrack?.name ?: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1294,7 +1311,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
|
|
||||||
currentlySelectedAlbum = albumProp
|
currentlySelectedAlbum = albumProp
|
||||||
|
|
||||||
registerPlaylist(albumDir.absolutePath, albumProp.fileToName, albumProp.shuffled, albumProp.diskJockeyingMode)
|
return registerPlaylist(albumDir.absolutePath, albumProp.fileToName, albumProp.shuffled, albumProp.diskJockeyingMode)
|
||||||
|
|
||||||
// scroll playlist to the page current song is
|
// scroll playlist to the page current song is
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package net.torvald.terrarum
|
package net.torvald.terrarum
|
||||||
|
|
||||||
open class MusicGovernor {
|
open class BackgroundMusicPlayer {
|
||||||
|
|
||||||
open fun update(ingameInstance: IngameInstance, delta: Float) {
|
open fun update(ingameInstance: IngameInstance, delta: Float) {
|
||||||
|
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
package net.torvald.terrarum
|
package net.torvald.terrarum
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx
|
|
||||||
import com.badlogic.gdx.Input
|
import com.badlogic.gdx.Input
|
||||||
import com.badlogic.gdx.utils.Disposable
|
|
||||||
import net.torvald.terrarum.App.printdbg
|
import net.torvald.terrarum.App.printdbg
|
||||||
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
||||||
import net.torvald.terrarum.audio.audiobank.MusicContainer
|
|
||||||
import net.torvald.terrarum.audio.dsp.BinoPan
|
|
||||||
import net.torvald.terrarum.gameactors.Actor
|
import net.torvald.terrarum.gameactors.Actor
|
||||||
import net.torvald.terrarum.gameactors.ActorID
|
import net.torvald.terrarum.gameactors.ActorID
|
||||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||||
@@ -37,7 +33,6 @@ import java.nio.file.Files
|
|||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.locks.Lock
|
import java.util.concurrent.locks.Lock
|
||||||
import java.util.function.Consumer
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -586,7 +581,7 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
|
|||||||
noticelet.sendNotification(itemID, itemCount)
|
noticelet.sendNotification(itemID, itemCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
open val musicGovernor: MusicGovernor = MusicGovernor()
|
open val backgroundMusicPlayer: BackgroundMusicPlayer = BackgroundMusicPlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun Lock.lock(body: () -> Unit) {
|
inline fun Lock.lock(body: () -> Unit) {
|
||||||
|
|||||||
259
src/net/torvald/terrarum/MusicService.kt
Normal file
259
src/net/torvald/terrarum/MusicService.kt
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
package net.torvald.terrarum
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To play the music, create a transaction then pass it to the `runTransaction(Transaction)`
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2024-06-28.
|
||||||
|
*/
|
||||||
|
object MusicService : TransactionListener() {
|
||||||
|
|
||||||
|
private var currentPlaylist: TerrarumMusicPlaylist? = null
|
||||||
|
|
||||||
|
override fun getCurrentStatusForTransaction(): TransactionState {
|
||||||
|
return TransactionState(
|
||||||
|
mutableMapOf(
|
||||||
|
"currentPlaylist" to currentPlaylist
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun commitTransaction(state: TransactionState) {
|
||||||
|
this.currentPlaylist = state["currentPlaylist"] as TerrarumMusicPlaylist?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the given playlist to this object if the transaction successes. If the given playlist is same as the
|
||||||
|
* current playlist, the transaction will successfully finish immediately; otherwise the given playlist will
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
* 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())`
|
||||||
|
*/
|
||||||
|
private fun createTransactionPlaylistChange(playlist: TerrarumMusicPlaylist, onSuccess: () -> Unit = {
|
||||||
|
App.audioMixer.startMusic(playlist.getCurrent())
|
||||||
|
}): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
var oldPlaylist: TerrarumMusicPlaylist? = null
|
||||||
|
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
oldPlaylist = state["currentPlaylist"] as TerrarumMusicPlaylist?
|
||||||
|
|
||||||
|
if (oldPlaylist == playlist) return
|
||||||
|
|
||||||
|
playlist.reset()
|
||||||
|
|
||||||
|
// request fadeout
|
||||||
|
if (App.audioMixer.musicTrack.isPlaying) {
|
||||||
|
var fadedOut = false
|
||||||
|
|
||||||
|
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
|
||||||
|
// put new playlist
|
||||||
|
state["currentPlaylist"] = playlist
|
||||||
|
|
||||||
|
fadedOut = true
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntil { fadedOut }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) {
|
||||||
|
oldPlaylist?.dispose()
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTransactionForNextMusicInPlaylist(onSuccess: () -> Unit): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
var fadedOut = false
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntil { fadedOut }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) {
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTransactionForPrevMusicInPlaylist(onSuccess: () -> Unit): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
var fadedOut = false
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntil { fadedOut }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) {
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTransactionForNthMusicInPlaylist(index: Int, onSuccess: () -> Unit): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
var fadedOut = false
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntil { fadedOut }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTransactionForPlaylistStop(onSuccess: () -> Unit): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
var fadedOut = false
|
||||||
|
// request fadeout
|
||||||
|
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
|
||||||
|
fadedOut = true
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntil { fadedOut }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTransactionForPlaylistResume(onSuccess: () -> Unit): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getCurrent())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTransactionPausePlaylistForMusicalFixture(
|
||||||
|
action: () -> Unit,
|
||||||
|
musicFinished: () -> Boolean,
|
||||||
|
onSuccess: () -> Unit,
|
||||||
|
onFailure: (Throwable) -> Unit
|
||||||
|
): Transaction {
|
||||||
|
return object : Transaction {
|
||||||
|
override fun start(state: TransactionState) {
|
||||||
|
var fadedOut = false
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
fadedOut = true
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntil { fadedOut }
|
||||||
|
|
||||||
|
// wait until the interjected music finishes
|
||||||
|
waitUntil { musicFinished() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||||
|
override fun onFailure(e: Throwable, state: TransactionState) { onFailure(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// note to self: wait() and notify() using a lock object is impractical as the Java thread can wake up
|
||||||
|
// randomly regardless of the notify(), which results in the common pattern of
|
||||||
|
// while (!condition) { lock.wait() }
|
||||||
|
// and if we need extra condition (i.e. musicFinished()), it's just a needlessly elaborate way of spinning,
|
||||||
|
// UNLESS THE THING MUST BE SYNCHRONISED WITH SOMETHING
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun waitUntil(escapeCondition: () -> Boolean) {
|
||||||
|
while (!escapeCondition()) {
|
||||||
|
Thread.sleep(4L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putNewPlaylist(playlist: TerrarumMusicPlaylist, onSuccess: (() -> Unit)? = null) {
|
||||||
|
if (onSuccess != null)
|
||||||
|
runTransaction(createTransactionPlaylistChange(playlist, onSuccess))
|
||||||
|
else
|
||||||
|
runTransaction(createTransactionPlaylistChange(playlist))
|
||||||
|
}
|
||||||
|
fun putNewPlaylist(playlist: TerrarumMusicPlaylist, onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||||
|
runTransaction(createTransactionPlaylistChange(playlist, onSuccess), onFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playMusicalFixture(action: () -> Unit, musicFinished: () -> Boolean, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) {
|
||||||
|
runTransaction(createTransactionPausePlaylistForMusicalFixture(action, musicFinished, onSuccess, onFailure))
|
||||||
|
}
|
||||||
|
fun playMusicalFixture(action: () -> Unit, musicFinished: () -> Boolean, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit, onFinally: () -> Unit = {}) {
|
||||||
|
runTransaction(createTransactionPausePlaylistForMusicalFixture(action, musicFinished, onSuccess, onFailure), onFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playNextSongInPlaylist(onSuccess: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForNextMusicInPlaylist(onSuccess))
|
||||||
|
}
|
||||||
|
fun playNextSongInPlaylist(onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForNextMusicInPlaylist(onSuccess), onFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playPrevSongInPlaylist(onSuccess: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForPrevMusicInPlaylist(onSuccess))
|
||||||
|
}
|
||||||
|
fun playPrevSongInPlaylist(onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForPrevMusicInPlaylist(onSuccess), onFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playNthSongInPlaylist(index: Int, onSuccess: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForNthMusicInPlaylist(index, onSuccess))
|
||||||
|
}
|
||||||
|
fun playNthSongInPlaylist(index: Int, onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForNthMusicInPlaylist(index, onSuccess), onFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopPlaylistPlayback(onSuccess: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForPlaylistStop(onSuccess))
|
||||||
|
}
|
||||||
|
fun stopPlaylistPlayback(onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForPlaylistStop(onSuccess), onFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resumePlaylistPlayback(onSuccess: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForPlaylistResume(onSuccess))
|
||||||
|
}
|
||||||
|
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||||
|
runTransaction(createTransactionForPlaylistResume(onSuccess), onFinally)
|
||||||
|
}
|
||||||
|
}
|
||||||
142
src/net/torvald/terrarum/TerrarumMusicPlaylist.kt
Normal file
142
src/net/torvald/terrarum/TerrarumMusicPlaylist.kt
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
init {
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
internalIndices.clear()
|
||||||
|
refillInternalIndices()
|
||||||
|
refillInternalIndices()
|
||||||
|
currentIndexCursor = musicList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkRefill() {
|
||||||
|
if (internalIndices.size < currentIndexCursor + 1)
|
||||||
|
refillInternalIndices()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCurrent(): MusicContainer {
|
||||||
|
checkRefill()
|
||||||
|
|
||||||
|
return musicList[currentIndexCursor]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNext(): MusicContainer {
|
||||||
|
checkRefill()
|
||||||
|
currentIndexCursor += 1
|
||||||
|
|
||||||
|
return musicList[currentIndexCursor]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPrev(): 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[currentIndexCursor]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun refillInternalIndices() {
|
||||||
|
internalIndices.addAll(musicList.indices.toMutableList().also { if (shuffled) it.shuffle() })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun getNthSong(n: Int) = musicList[n]
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
musicList.forEach {
|
||||||
|
it.tryDispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 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 }?.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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -529,7 +529,11 @@ class AudioMixer : Disposable {
|
|||||||
// fade will be processed by the update()
|
// fade will be processed by the update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestFadeOut(track: TerrarumAudioMixerTrack, length: Double = DEFAULT_FADEOUT_LEN, target: Double = 0.0, source: Double? = null, jobAfterFadeout: () -> Unit = {}) {
|
/**
|
||||||
|
* Preferably, audio apps should NOT call this function directly to change music, [MusicService] must be used
|
||||||
|
* to control the music playback instead.
|
||||||
|
*/
|
||||||
|
internal fun requestFadeOut(track: TerrarumAudioMixerTrack, length: Double = DEFAULT_FADEOUT_LEN, target: Double = 0.0, source: Double? = null, jobAfterFadeout: () -> Unit = {}) {
|
||||||
val req = fadeReqs[track]!!
|
val req = fadeReqs[track]!!
|
||||||
if (!req.fadeoutFired) {
|
if (!req.fadeoutFired) {
|
||||||
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||||
@@ -541,7 +545,11 @@ class AudioMixer : Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestFadeIn(track: TerrarumAudioMixerTrack, length: Double, target: Double = 1.0, source: Double? = null, jobAfterFadeout: () -> Unit = {}) {
|
/**
|
||||||
|
* Preferably, audio apps should NOT call this function directly to change music, [MusicService] must be used
|
||||||
|
* to control the music playback instead.
|
||||||
|
*/
|
||||||
|
internal fun requestFadeIn(track: TerrarumAudioMixerTrack, length: Double, target: Double = 1.0, source: Double? = null, jobAfterFadeout: () -> Unit = {}) {
|
||||||
// printdbg(this, "fadein called by")
|
// printdbg(this, "fadein called by")
|
||||||
// printStackTrace(this)
|
// printStackTrace(this)
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
|
|
||||||
lateinit var gameWorld: GameWorld
|
lateinit var gameWorld: GameWorld
|
||||||
|
|
||||||
override val musicGovernor = TerrarumMusicGovernor()
|
override val backgroundMusicPlayer = TerrarumBackgroundMusicPlayer()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
gameUpdateGovernor = ConsistentUpdateRate
|
gameUpdateGovernor = ConsistentUpdateRate
|
||||||
@@ -396,7 +396,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
musicGovernor.update(this, delta)
|
backgroundMusicPlayer.update(this, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -495,7 +495,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
// blockMarkings.dispose()
|
// blockMarkings.dispose()
|
||||||
uiPenMenu.dispose()
|
uiPenMenu.dispose()
|
||||||
uiGetPoiName.dispose()
|
uiGetPoiName.dispose()
|
||||||
musicGovernor.dispose()
|
backgroundMusicPlayer.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPoiNameForExport(w: Int, h: Int, callback: (String) -> Unit) {
|
fun getPoiNameForExport(w: Int, h: Int, callback: (String) -> Unit) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import net.torvald.terrarum.audio.audiobank.MusicContainer
|
|||||||
import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH
|
import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class TerrarumMusicGovernor : MusicGovernor() {
|
class TerrarumBackgroundMusicPlayer : BackgroundMusicPlayer() {
|
||||||
private val STATE_INIT = 0
|
private val STATE_INIT = 0
|
||||||
private val STATE_FIREPLAY = 1
|
private val STATE_FIREPLAY = 1
|
||||||
private val STATE_PLAYING = 2
|
private val STATE_PLAYING = 2
|
||||||
@@ -22,7 +22,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
musicState = STATE_INTERMISSION
|
musicState = STATE_INTERMISSION
|
||||||
}
|
}
|
||||||
|
|
||||||
private var songs: List<MusicContainer> = emptyList()
|
private var playlist: List<MusicContainer> = emptyList()
|
||||||
var playlistName = ""; private set
|
var playlistName = ""; private set
|
||||||
/** canonicalPath with path separators converted to forward slash */
|
/** canonicalPath with path separators converted to forward slash */
|
||||||
var playlistSource = "" ; private set
|
var playlistSource = "" ; private set
|
||||||
@@ -42,7 +42,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
|
|
||||||
playlistName = musicDir.substringAfterLast('/')
|
playlistName = musicDir.substringAfterLast('/')
|
||||||
|
|
||||||
songs = File(musicDir).listFiles()?.sortedBy { it.name }?.mapNotNull {
|
playlist = File(musicDir).listFiles()?.sortedBy { it.name }?.mapNotNull {
|
||||||
printdbg(this, "Music: ${it.absolutePath}")
|
printdbg(this, "Music: ${it.absolutePath}")
|
||||||
try {
|
try {
|
||||||
MusicContainer(
|
MusicContainer(
|
||||||
@@ -67,77 +67,91 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun restockMusicBin() {
|
private fun restockMusicBin() {
|
||||||
musicBin = ArrayList(if (shuffled) songs.shuffled() else songs.slice(songs.indices))
|
musicBin = ArrayList(if (shuffled) playlist.shuffled() else playlist.slice(playlist.indices))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Send the playlist to the MusicService to be played at the other play commands.
|
||||||
|
*
|
||||||
* @param musicDir where the music files are. Absolute path.
|
* @param musicDir where the music files are. Absolute path.
|
||||||
* @param shuffled if the tracks are to be shuffled
|
* @param shuffled if the tracks are to be shuffled
|
||||||
* @param diskJockeyingMode `intermittent` to give random gap between tracks, `continuous` for continuous playback
|
* @param diskJockeyingMode `intermittent` to give random gap between tracks, `continuous` for continuous playback
|
||||||
*/
|
*/
|
||||||
fun queueDirectory(musicDir: String, shuffled: Boolean, diskJockeyingMode: String, fileToName: ((String) -> String)? = null) {
|
fun queueDirectory(musicDir: String, shuffled: Boolean, diskJockeyingMode: String, playImmediately: Boolean, fileToName: ((String) -> String) = { name: String ->
|
||||||
if (musicState != STATE_INIT && musicState != STATE_INTERMISSION) {
|
name.substringBeforeLast('.').replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" ")
|
||||||
|
}): TerrarumMusicPlaylist {
|
||||||
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
|
/*if (musicState != STATE_INIT && musicState != STATE_INTERMISSION) {
|
||||||
App.audioMixer.requestFadeOut(App.audioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits
|
App.audioMixer.requestFadeOut(App.audioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits
|
||||||
stopMusic0(App.audioMixer.musicTrack.currentTrack)
|
stopMusic0(App.audioMixer.musicTrack.currentTrack)
|
||||||
}
|
}
|
||||||
|
|
||||||
songs.forEach { it.tryDispose() }
|
playlist.forEach { it.tryDispose() }
|
||||||
registerSongsFromDir(musicDir, fileToName)
|
registerSongsFromDir(musicDir, fileToName)
|
||||||
|
|
||||||
this.shuffled = shuffled
|
this.shuffled = shuffled
|
||||||
this.diskJockeyingMode = diskJockeyingMode
|
this.diskJockeyingMode = diskJockeyingMode
|
||||||
|
|
||||||
restockMusicBin()
|
restockMusicBin()*/
|
||||||
|
|
||||||
|
val playlist = TerrarumMusicPlaylist.fromDirectory(musicDir, shuffled, diskJockeyingMode, fileToName)
|
||||||
|
|
||||||
|
if (!playImmediately)
|
||||||
|
MusicService.putNewPlaylist(playlist) {}
|
||||||
|
else
|
||||||
|
MusicService.putNewPlaylist(playlist)
|
||||||
|
|
||||||
|
return playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a song to the head of the internal playlist (`musicBin`)
|
* Adds a song to the head of the internal playlist (`musicBin`)
|
||||||
*/
|
*/
|
||||||
fun queueMusicToPlayNext(music: MusicContainer) {
|
fun xxxqueueMusicToPlayNext(music: MusicContainer) {
|
||||||
musicBin.add(0, music)
|
musicBin.add(0, music)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unshifts an internal playlist (`musicBin`). The `music` argument must be the song that exists on the `songs`.
|
* Unshifts an internal playlist (`musicBin`). The `music` argument must be the song that exists on the `songs`.
|
||||||
*/
|
*/
|
||||||
fun unshiftPlaylist(music: MusicContainer) {
|
fun xxxunshiftPlaylist(music: MusicContainer) {
|
||||||
val indexAtMusicBin = songs.indexOf(music)
|
val indexAtMusicBin = playlist.indexOf(music)
|
||||||
if (indexAtMusicBin < 0) throw IllegalArgumentException("The music does not exist on the internal songs list ($music)")
|
if (indexAtMusicBin < 0) throw IllegalArgumentException("The music does not exist on the internal songs list ($music)")
|
||||||
|
|
||||||
// rewrite musicBin
|
// rewrite musicBin
|
||||||
val newMusicBin = if (shuffled) songs.shuffled().toTypedArray().also {
|
val newMusicBin = if (shuffled) playlist.shuffled().toTypedArray().also {
|
||||||
// if shuffled,
|
// if shuffled,
|
||||||
// 1. create a shuffled version of songlist
|
// 1. create a shuffled version of songlist
|
||||||
// 2. swap two songs such that the songs[indexAtMusicBin] comes first
|
// 2. swap two songs such that the songs[indexAtMusicBin] comes first
|
||||||
val swapTo = it.indexOf(songs[indexAtMusicBin])
|
val swapTo = it.indexOf(playlist[indexAtMusicBin])
|
||||||
val tmp = it[swapTo]
|
val tmp = it[swapTo]
|
||||||
it[swapTo] = it[0]
|
it[swapTo] = it[0]
|
||||||
it[0] = tmp
|
it[0] = tmp
|
||||||
}
|
}
|
||||||
else Array(songs.size - indexAtMusicBin) { offset ->
|
else Array(playlist.size - indexAtMusicBin) { offset ->
|
||||||
val k = offset + indexAtMusicBin
|
val k = offset + indexAtMusicBin
|
||||||
songs[k]
|
playlist[k]
|
||||||
}
|
}
|
||||||
|
|
||||||
musicBin = ArrayList(newMusicBin.toList())
|
musicBin = ArrayList(newMusicBin.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queueIndexFromPlaylist(indexAtMusicBin: Int) {
|
fun xxxqueueIndexFromPlaylist(indexAtMusicBin: Int) {
|
||||||
if (indexAtMusicBin !in songs.indices) throw IndexOutOfBoundsException("The index is outside of the internal songs list ($indexAtMusicBin/${songs.size})")
|
if (indexAtMusicBin !in playlist.indices) throw IndexOutOfBoundsException("The index is outside of the internal songs list ($indexAtMusicBin/${playlist.size})")
|
||||||
|
|
||||||
// rewrite musicBin
|
// rewrite musicBin
|
||||||
val newMusicBin = if (shuffled) songs.shuffled().toTypedArray().also {
|
val newMusicBin = if (shuffled) playlist.shuffled().toTypedArray().also {
|
||||||
// if shuffled,
|
// if shuffled,
|
||||||
// 1. create a shuffled version of songlist
|
// 1. create a shuffled version of songlist
|
||||||
// 2. swap two songs such that the songs[indexAtMusicBin] comes first
|
// 2. swap two songs such that the songs[indexAtMusicBin] comes first
|
||||||
val swapTo = it.indexOf(songs[indexAtMusicBin])
|
val swapTo = it.indexOf(playlist[indexAtMusicBin])
|
||||||
val tmp = it[swapTo]
|
val tmp = it[swapTo]
|
||||||
it[swapTo] = it[0]
|
it[swapTo] = it[0]
|
||||||
it[0] = tmp
|
it[0] = tmp
|
||||||
}
|
}
|
||||||
else Array(songs.size - indexAtMusicBin) { offset ->
|
else Array(playlist.size - indexAtMusicBin) { offset ->
|
||||||
val k = offset + indexAtMusicBin
|
val k = offset + indexAtMusicBin
|
||||||
songs[k]
|
playlist[k]
|
||||||
}
|
}
|
||||||
|
|
||||||
musicBin = ArrayList(newMusicBin.toList())
|
musicBin = ArrayList(newMusicBin.toList())
|
||||||
@@ -162,7 +176,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
private val musicStopHooks = ArrayList<(MusicContainer) -> Unit>()
|
private val musicStopHooks = ArrayList<(MusicContainer) -> Unit>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
queueDirectory(App.customMusicDir, true, "intermittent")
|
queueDirectory(App.customMusicDir, true, "intermittent", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -175,7 +189,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
songs.forEach {
|
playlist.forEach {
|
||||||
App.disposables.add(it)
|
App.disposables.add(it)
|
||||||
}
|
}
|
||||||
ambients.forEach { (k, v) ->
|
ambients.forEach { (k, v) ->
|
||||||
@@ -218,7 +232,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
val timeNow = System.currentTimeMillis()
|
val timeNow = System.currentTimeMillis()
|
||||||
val trackThis = App.audioMixer.musicTrack.currentTrack
|
val trackThis = App.audioMixer.musicTrack.currentTrack
|
||||||
|
|
||||||
if (caller is TerrarumMusicGovernor) {
|
if (caller is TerrarumBackgroundMusicPlayer) {
|
||||||
if (stopCaller == null) {
|
if (stopCaller == null) {
|
||||||
// printdbg(this, "Caller: this, prev caller: $stopCaller, len: $pauseLen, obliging stop request")
|
// printdbg(this, "Caller: this, prev caller: $stopCaller, len: $pauseLen, obliging stop request")
|
||||||
stopMusic0(trackThis, callStopMusicHook, pauseLen)
|
stopMusic0(trackThis, callStopMusicHook, pauseLen)
|
||||||
@@ -316,7 +330,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
// start the song queueing if there is one to play
|
// start the song queueing if there is one to play
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
firstTime = false
|
firstTime = false
|
||||||
if (songs.isNotEmpty()) musicState = STATE_INTERMISSION
|
if (playlist.isNotEmpty()) musicState = STATE_INTERMISSION
|
||||||
if (ambients.isNotEmpty()) ambState = STATE_INTERMISSION
|
if (ambients.isNotEmpty()) ambState = STATE_INTERMISSION
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +348,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
STATE_INTERMISSION -> {
|
STATE_INTERMISSION -> {
|
||||||
intermissionAkku += delta
|
intermissionAkku += delta
|
||||||
|
|
||||||
if (intermissionAkku >= intermissionLength && songs.isNotEmpty()) {
|
if (intermissionAkku >= intermissionLength && playlist.isNotEmpty()) {
|
||||||
intermissionAkku = 0f
|
intermissionAkku = 0f
|
||||||
musicState = STATE_FIREPLAY
|
musicState = STATE_FIREPLAY
|
||||||
}
|
}
|
||||||
@@ -262,7 +262,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
override var gameFullyLoaded = false
|
override var gameFullyLoaded = false
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
override val musicGovernor = TerrarumMusicGovernor()
|
override val backgroundMusicPlayer = TerrarumBackgroundMusicPlayer()
|
||||||
|
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
@@ -1006,7 +1006,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
oldSelectedWireRenderClass = selectedWireRenderClass
|
oldSelectedWireRenderClass = selectedWireRenderClass
|
||||||
}
|
}
|
||||||
|
|
||||||
musicGovernor.update(this, delta)
|
backgroundMusicPlayer.update(this, delta)
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// ui-related updates //
|
// ui-related updates //
|
||||||
@@ -1795,7 +1795,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
catch (e: IllegalArgumentException) {}
|
catch (e: IllegalArgumentException) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
musicGovernor.dispose()
|
backgroundMusicPlayer.dispose()
|
||||||
super.dispose()
|
super.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameactors
|
package net.torvald.terrarum.modulebasegame.gameactors
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
@@ -10,7 +9,6 @@ import net.torvald.terrarum.*
|
|||||||
import net.torvald.terrarum.App.printdbg
|
import net.torvald.terrarum.App.printdbg
|
||||||
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
||||||
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
|
||||||
import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN
|
|
||||||
import net.torvald.terrarum.audio.audiobank.MusicContainer
|
import net.torvald.terrarum.audio.audiobank.MusicContainer
|
||||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
|
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
|
||||||
import net.torvald.terrarum.audio.dsp.NullFilter
|
import net.torvald.terrarum.audio.dsp.NullFilter
|
||||||
@@ -21,7 +19,7 @@ import net.torvald.terrarum.gameactors.Hitbox
|
|||||||
import net.torvald.terrarum.gameactors.Lightbox
|
import net.torvald.terrarum.gameactors.Lightbox
|
||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
import net.torvald.terrarum.langpack.Lang
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumMusicGovernor
|
import net.torvald.terrarum.modulebasegame.TerrarumBackgroundMusicPlayer
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef
|
import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.MusicDiscHelper
|
import net.torvald.terrarum.modulebasegame.gameitems.MusicDiscHelper
|
||||||
@@ -108,7 +106,7 @@ class FixtureJukebox : Electric, PlaysMusic {
|
|||||||
|
|
||||||
// supress the normal background music playback
|
// supress the normal background music playback
|
||||||
if (musicIsPlaying && !flagDespawn) {
|
if (musicIsPlaying && !flagDespawn) {
|
||||||
(INGAME.musicGovernor as TerrarumMusicGovernor).stopMusic(this, true)
|
(INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).stopMusic(this, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,27 +125,49 @@ class FixtureJukebox : Electric, PlaysMusic {
|
|||||||
|
|
||||||
printdbg(this, "Title: $title, artist: $artist")
|
printdbg(this, "Title: $title, artist: $artist")
|
||||||
|
|
||||||
musicNowPlaying = MusicContainer(title, musicFile.file()) {
|
val returnToInitialState = {
|
||||||
unloadEffector(musicNowPlaying)
|
unloadEffector(musicNowPlaying)
|
||||||
discCurrentlyPlaying = null
|
discCurrentlyPlaying = null
|
||||||
musicNowPlaying?.tryDispose()
|
musicNowPlaying?.tryDispose()
|
||||||
musicNowPlaying = null
|
musicNowPlaying = null
|
||||||
|
|
||||||
printdbg(this, "Stop music $title - $artist")
|
|
||||||
|
|
||||||
// can't call stopDiscPlayback() because of the recursion
|
|
||||||
|
|
||||||
(INGAME.musicGovernor as TerrarumMusicGovernor).stopMusic(this, pauseLen = (INGAME.musicGovernor as TerrarumMusicGovernor).getRandomMusicInterval())
|
|
||||||
|
|
||||||
backLamp.currentFrame = 0
|
backLamp.currentFrame = 0
|
||||||
playMech.currentFrame = 0
|
playMech.currentFrame = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
musicNowPlaying = MusicContainer(title, musicFile.file()) {
|
||||||
|
printdbg(this, "Stop music $title - $artist")
|
||||||
|
|
||||||
|
// can't call stopDiscPlayback() because of the recursion
|
||||||
|
(INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).stopMusic(this, pauseLen = (INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).getRandomMusicInterval())
|
||||||
|
}
|
||||||
|
|
||||||
discCurrentlyPlaying = index
|
discCurrentlyPlaying = index
|
||||||
|
|
||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2f) {
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
|
/*App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2f) {
|
||||||
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
MusicService.playMusicalFixture(
|
||||||
|
// action: () -> Unit
|
||||||
|
{
|
||||||
|
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
||||||
|
},
|
||||||
|
// musicFinished: () -> Boolean
|
||||||
|
{
|
||||||
|
!musicIsPlaying
|
||||||
|
},
|
||||||
|
// onSuccess: () -> Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
// onFailure: (Throwable) -> Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
// onFinally: () -> Unit
|
||||||
|
returnToInitialState
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
backLamp.currentFrame = 1 + (index / 2)
|
backLamp.currentFrame = 1 + (index / 2)
|
||||||
@@ -165,7 +185,7 @@ class FixtureJukebox : Electric, PlaysMusic {
|
|||||||
*/
|
*/
|
||||||
fun stopGracefully() {
|
fun stopGracefully() {
|
||||||
stopDiscPlayback()
|
stopDiscPlayback()
|
||||||
(INGAME.musicGovernor as TerrarumMusicGovernor).stopMusic(this, pauseLen = (INGAME.musicGovernor as TerrarumMusicGovernor).getRandomMusicInterval())
|
(INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).stopMusic(this, pauseLen = (INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).getRandomMusicInterval())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import net.torvald.terrarum.gameactors.AVKey
|
|||||||
import net.torvald.terrarum.gameitems.GameItem
|
import net.torvald.terrarum.gameitems.GameItem
|
||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
import net.torvald.terrarum.langpack.Lang
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumMusicGovernor
|
import net.torvald.terrarum.modulebasegame.TerrarumBackgroundMusicPlayer
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef
|
import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.MusicDiscHelper
|
import net.torvald.terrarum.modulebasegame.gameitems.MusicDiscHelper
|
||||||
@@ -96,7 +96,7 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
|||||||
|
|
||||||
// supress the normal background music playback
|
// supress the normal background music playback
|
||||||
if (musicIsPlaying && !flagDespawn) {
|
if (musicIsPlaying && !flagDespawn) {
|
||||||
(INGAME.musicGovernor as TerrarumMusicGovernor).stopMusic(this, true)
|
(INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).stopMusic(this, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,21 +111,47 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
|||||||
|
|
||||||
App.printdbg(this, "Title: $title, artist: $artist")
|
App.printdbg(this, "Title: $title, artist: $artist")
|
||||||
|
|
||||||
musicNowPlaying = MusicContainer(title, musicFile.file()) {
|
val returnToInitialState = {
|
||||||
unloadEffector(musicNowPlaying)
|
unloadEffector(musicNowPlaying)
|
||||||
musicNowPlaying?.tryDispose()
|
musicNowPlaying?.tryDispose()
|
||||||
musicNowPlaying = null
|
musicNowPlaying = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
musicNowPlaying = MusicContainer(title, musicFile.file()) {
|
||||||
App.printdbg(this, "Stop music $title - $artist")
|
App.printdbg(this, "Stop music $title - $artist")
|
||||||
|
|
||||||
// can't call stopDiscPlayback() because of the recursion
|
// can't call stopDiscPlayback() because of the recursion
|
||||||
|
(INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).stopMusic(this, pauseLen = (INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).getRandomMusicInterval())
|
||||||
(INGAME.musicGovernor as TerrarumMusicGovernor).stopMusic(this, pauseLen = (INGAME.musicGovernor as TerrarumMusicGovernor).getRandomMusicInterval())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 2f) {
|
|
||||||
|
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||||
|
/*App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 2f) {
|
||||||
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
MusicService.playMusicalFixture(
|
||||||
|
// action: () -> Unit
|
||||||
|
{
|
||||||
|
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
||||||
|
},
|
||||||
|
// musicFinished: () -> Boolean
|
||||||
|
{
|
||||||
|
!musicIsPlaying
|
||||||
|
},
|
||||||
|
// onSuccess: () -> Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
// onFailure: (Throwable) -> Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
// onFinally: () -> Unit
|
||||||
|
returnToInitialState
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
(sprite as SheetSpriteAnimation).currentRow = 0
|
(sprite as SheetSpriteAnimation).currentRow = 0
|
||||||
}
|
}
|
||||||
@@ -137,7 +163,7 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
|||||||
*/
|
*/
|
||||||
fun stopGracefully() {
|
fun stopGracefully() {
|
||||||
stopDiscPlayback()
|
stopDiscPlayback()
|
||||||
(INGAME.musicGovernor as TerrarumMusicGovernor).stopMusic(this, pauseLen = (INGAME.musicGovernor as TerrarumMusicGovernor).getRandomMusicInterval())
|
(INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).stopMusic(this, pauseLen = (INGAME.backgroundMusicPlayer as TerrarumBackgroundMusicPlayer).getRandomMusicInterval())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
76
src/net/torvald/terrarum/transaction/Transaction.kt
Normal file
76
src/net/torvald/terrarum/transaction/Transaction.kt
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package net.torvald.terrarum.transaction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2024-06-28.
|
||||||
|
*/
|
||||||
|
interface Transaction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to begin the transaction.
|
||||||
|
*
|
||||||
|
* When started using [TransactionListener.runTransaction], the transaction runs on a separate thread,
|
||||||
|
* and thus any operation that requires GL Context will fail.
|
||||||
|
*/
|
||||||
|
fun start(state: TransactionState)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by [TransactionListener.runTransaction], when the transaction was successful.
|
||||||
|
*/
|
||||||
|
fun onSuccess(state: TransactionState)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by [TransactionListener.runTransaction], when the transaction failed.
|
||||||
|
*/
|
||||||
|
fun onFailure(e: Throwable, state: TransactionState)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class TransactionListener {
|
||||||
|
|
||||||
|
/** `null` if not locked, a class that acquired the lock if locked */
|
||||||
|
var transactionLockedBy: Any? = null; private set
|
||||||
|
val transactionLocked: Boolean; get() = (transactionLockedBy != null)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transaction modifies a given state to a new state, then applies the new state to the object.
|
||||||
|
* The given `transaction` may only modify values which is passed to it.
|
||||||
|
*/
|
||||||
|
fun runTransaction(transaction: Transaction, onFinally: () -> Unit = {}) {
|
||||||
|
Thread { synchronized(this) {
|
||||||
|
val state = getCurrentStatusForTransaction()
|
||||||
|
if (!transactionLocked) {
|
||||||
|
try {
|
||||||
|
transaction.start(state)
|
||||||
|
// if successful:
|
||||||
|
commitTransaction(state)
|
||||||
|
// notify the success
|
||||||
|
transaction.onSuccess(state)
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
// if failed, notify the failure
|
||||||
|
transaction.onFailure(e, state)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
onFinally()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
transaction.onFailure(LockedException(this, transactionLockedBy), state)
|
||||||
|
}
|
||||||
|
} }.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun getCurrentStatusForTransaction(): TransactionState
|
||||||
|
protected abstract fun commitTransaction(state: TransactionState)
|
||||||
|
}
|
||||||
|
|
||||||
|
class LockedException(listener: TransactionListener, lockedBy: Any?) :
|
||||||
|
Exception("Transaction is rejected because the class '${listener.javaClass.canonicalName}' is locked by '${lockedBy?.javaClass?.canonicalName}'")
|
||||||
|
|
||||||
|
@JvmInline value class TransactionState(val valueTable: MutableMap<String, Any?>) {
|
||||||
|
operator fun get(key: String) = valueTable[key]
|
||||||
|
operator fun set(key: String, value: Any?) {
|
||||||
|
valueTable[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,11 +96,11 @@ class ArrayListMap<K, V> : MutableMap<K, V> {
|
|||||||
return super.computeIfAbsent(key, mappingFunction)
|
return super.computeIfAbsent(key, mappingFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun computeIfPresent(key: K, remappingFunction: BiFunction<in K, in V, out V?>): V? {
|
override fun computeIfPresent(key: K, remappingFunction: BiFunction<in K, in V & Any, out V?>): V? {
|
||||||
return super.computeIfPresent(key, remappingFunction)
|
return super.computeIfPresent(key, remappingFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun merge(key: K, value: V, remappingFunction: BiFunction<in V, in V, out V?>): V? {
|
override fun merge(key: K, value: V & Any, remappingFunction: BiFunction<in V & Any, in V & Any, out V?>): V? {
|
||||||
return super.merge(key, value, remappingFunction)
|
return super.merge(key, value, remappingFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user