Currently playing music track displayed under Options-Sound (#5357)

This commit is contained in:
SomeTroglodyte
2021-09-30 05:35:20 +02:00
committed by GitHub
parent 9fe19d0d52
commit 03f374b058
4 changed files with 76 additions and 12 deletions

View File

@ -474,6 +474,7 @@ Turns between autosaves =
Sound effects volume =
Music volume =
Pause between tracks =
Currently playing: [title] =
Download music =
Downloading... =
Could not download music! =

View File

@ -78,13 +78,39 @@ class MusicController {
/** Keeps paths of recently played track to reduce repetition */
private val musicHistory = ArrayDeque<String>(musicHistorySize)
/** One potential listener gets notified when track changes */
private var onTrackChangeListener: ((String)->Unit)? = null
//endregion
//region Pure functions
/** @return the path of the playing track or null if none playing */
fun currentlyPlaying() = if (state != ControllerState.Playing && state != ControllerState.PlaySingle) null
private fun currentlyPlaying(): String = if (state != ControllerState.Playing && state != ControllerState.PlaySingle) ""
else musicHistory.peekLast()
/** Registers a callback that will be called with the new track name every time it changes.
* The track name will be prettified ("Modname: Track" instead of "mods/Modname/music/Track.ogg").
*
* Will be called on a background thread, so please decouple UI access on the receiving side.
*/
fun onChange(listener: ((String)->Unit)?) {
onTrackChangeListener = listener
fireOnChange()
}
private fun fireOnChange() {
val fileName = currentlyPlaying()
if (fileName.isEmpty()) {
onTrackChangeListener?.invoke(fileName)
return
}
val fileNameParts = fileName.split('/')
val modName = if (fileNameParts.size > 1 && fileNameParts[0] == "mods") fileNameParts[1] else ""
var trackName = fileNameParts[if (fileNameParts.size > 3 && fileNameParts[2] == "music") 3 else 1]
for (extension in fileExtensions)
trackName = trackName.removeSuffix(".$extension")
onTrackChangeListener?.invoke(modName + (if (modName.isEmpty()) "" else ": ") + trackName)
}
/**
* Determines whether any music tracks are available for the options menu
*/
@ -112,12 +138,15 @@ class MusicController {
// no music to play - begin silence or shut down
ticksOfSilence = 0
state = if (state == ControllerState.PlaySingle) ControllerState.Shutdown else ControllerState.Silence
fireOnChange()
} else if (next!!.state.canPlay) {
// Next track - if top slot empty and a next exists, move it to top and start
current = next
next = null
if (!current!!.play())
state = ControllerState.Shutdown
else
fireOnChange()
} // else wait for the thread of next.load() to finish
} else if (!current!!.isPlaying()) {
// normal end of track
@ -133,10 +162,13 @@ class MusicController {
ticksOfSilence = 0
chooseTrack()
}
ControllerState.Shutdown, ControllerState.Idle -> {
state = ControllerState.Idle
shutdown()
ControllerState.Shutdown -> {
// Fade out first, when all queue entries are idle set up for real shutdown
if (current?.shutdownTick() != false && next?.shutdownTick() != false)
state = ControllerState.Idle
}
ControllerState.Idle ->
shutdown() // stops timer so this will not repeat
ControllerState.Pause ->
current?.timerTick()
}
@ -311,8 +343,10 @@ class MusicController {
}
/** Forceful shutdown of music playback and timers - see [gracefulShutdown] */
fun shutdown() {
private fun shutdown() {
state = ControllerState.Idle
fireOnChange()
onTrackChangeListener = null
if (musicTimer != null) {
musicTimer!!.cancel()
musicTimer = null

View File

@ -99,14 +99,17 @@ class MusicTrackController(private var volume: Float) {
}
private fun fadeOutStep() {
// fade-out: linearly ramp fadeVolume to 0.0, then act according to Status (Playing->Silence/Pause/Shutdown)
// This needs to guard against the music backend breaking mid-fade away during game shutdown
fadeVolume -= fadeStep
if (fadeVolume >= 0.001f && music != null && music!!.isPlaying) {
music!!.volume = volume * fadeVolume
return
}
fadeVolume = 0f
music!!.volume = 0f
music!!.pause()
try {
if (fadeVolume >= 0.001f && music != null && music!!.isPlaying) {
music!!.volume = volume * fadeVolume
return
}
fadeVolume = 0f
music!!.volume = 0f
music!!.pause()
} catch (_: Throwable) {}
state = State.Idle
}
@ -121,6 +124,19 @@ class MusicTrackController(private var volume: Float) {
state = fade
}
/** Graceful shutdown tick event - fade out then report Idle
* @return `true` shutdown can proceed, `false` still fading out
*/
fun shutdownTick(): Boolean {
if (!state.canPlay) state = State.Idle
if (state == State.Idle) return true
if (state != State.FadeOut) {
state = State.FadeOut
fadeStep = MusicController.defaultFadingStep
}
return timerTick() == State.Idle
}
/** @return [Music.isPlaying] (Gdx music stream is playing) unless [state] says it won't make sense */
fun isPlaying() = state.canPlay && music?.isPlaying == true

View File

@ -84,6 +84,7 @@ class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousSc
}
addCloseButton {
previousScreen.game.musicController.onChange(null)
previousScreen.game.limitOrientationsHelper?.allowPortrait(settings.allowAndroidPortrait)
if (previousScreen is WorldScreen)
previousScreen.enableNextTurnButtonAfterOptions()
@ -210,6 +211,7 @@ class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousSc
if (previousScreen.game.musicController.isMusicAvailable()) {
addMusicVolumeSlider()
addMusicPauseSlider()
addMusicCurrentlyPlaying()
} else {
addDownloadMusic()
}
@ -456,6 +458,17 @@ class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousSc
add(pauseLengthSlider).pad(5f).row()
}
private fun Table.addMusicCurrentlyPlaying() {
val label = WrappableLabel("", this.width - 10f, Color(-0x2f5001), 16)
label.wrap = true
add(label).padTop(20f).colspan(2).fillX().row()
previousScreen.game.musicController.onChange {
Gdx.app.postRunnable {
label.setText("Currently playing: [$it]".tr())
}
}
}
private fun Table.addDownloadMusic() {
val downloadMusicButton = "Download music".toTextButton()
add(downloadMusicButton).colspan(2).row()