mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 14:24:43 -04:00
Currently playing music track displayed under Options-Sound (#5357)
This commit is contained in:
parent
9fe19d0d52
commit
03f374b058
@ -474,6 +474,7 @@ Turns between autosaves =
|
|||||||
Sound effects volume =
|
Sound effects volume =
|
||||||
Music volume =
|
Music volume =
|
||||||
Pause between tracks =
|
Pause between tracks =
|
||||||
|
Currently playing: [title] =
|
||||||
Download music =
|
Download music =
|
||||||
Downloading... =
|
Downloading... =
|
||||||
Could not download music! =
|
Could not download music! =
|
||||||
|
@ -78,13 +78,39 @@ class MusicController {
|
|||||||
/** Keeps paths of recently played track to reduce repetition */
|
/** Keeps paths of recently played track to reduce repetition */
|
||||||
private val musicHistory = ArrayDeque<String>(musicHistorySize)
|
private val musicHistory = ArrayDeque<String>(musicHistorySize)
|
||||||
|
|
||||||
|
/** One potential listener gets notified when track changes */
|
||||||
|
private var onTrackChangeListener: ((String)->Unit)? = null
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region Pure functions
|
//region Pure functions
|
||||||
|
|
||||||
/** @return the path of the playing track or null if none playing */
|
/** @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()
|
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
|
* 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
|
// no music to play - begin silence or shut down
|
||||||
ticksOfSilence = 0
|
ticksOfSilence = 0
|
||||||
state = if (state == ControllerState.PlaySingle) ControllerState.Shutdown else ControllerState.Silence
|
state = if (state == ControllerState.PlaySingle) ControllerState.Shutdown else ControllerState.Silence
|
||||||
|
fireOnChange()
|
||||||
} else if (next!!.state.canPlay) {
|
} else if (next!!.state.canPlay) {
|
||||||
// Next track - if top slot empty and a next exists, move it to top and start
|
// Next track - if top slot empty and a next exists, move it to top and start
|
||||||
current = next
|
current = next
|
||||||
next = null
|
next = null
|
||||||
if (!current!!.play())
|
if (!current!!.play())
|
||||||
state = ControllerState.Shutdown
|
state = ControllerState.Shutdown
|
||||||
|
else
|
||||||
|
fireOnChange()
|
||||||
} // else wait for the thread of next.load() to finish
|
} // else wait for the thread of next.load() to finish
|
||||||
} else if (!current!!.isPlaying()) {
|
} else if (!current!!.isPlaying()) {
|
||||||
// normal end of track
|
// normal end of track
|
||||||
@ -133,10 +162,13 @@ class MusicController {
|
|||||||
ticksOfSilence = 0
|
ticksOfSilence = 0
|
||||||
chooseTrack()
|
chooseTrack()
|
||||||
}
|
}
|
||||||
ControllerState.Shutdown, ControllerState.Idle -> {
|
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
|
state = ControllerState.Idle
|
||||||
shutdown()
|
|
||||||
}
|
}
|
||||||
|
ControllerState.Idle ->
|
||||||
|
shutdown() // stops timer so this will not repeat
|
||||||
ControllerState.Pause ->
|
ControllerState.Pause ->
|
||||||
current?.timerTick()
|
current?.timerTick()
|
||||||
}
|
}
|
||||||
@ -311,8 +343,10 @@ class MusicController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Forceful shutdown of music playback and timers - see [gracefulShutdown] */
|
/** Forceful shutdown of music playback and timers - see [gracefulShutdown] */
|
||||||
fun shutdown() {
|
private fun shutdown() {
|
||||||
state = ControllerState.Idle
|
state = ControllerState.Idle
|
||||||
|
fireOnChange()
|
||||||
|
onTrackChangeListener = null
|
||||||
if (musicTimer != null) {
|
if (musicTimer != null) {
|
||||||
musicTimer!!.cancel()
|
musicTimer!!.cancel()
|
||||||
musicTimer = null
|
musicTimer = null
|
||||||
|
@ -99,7 +99,9 @@ class MusicTrackController(private var volume: Float) {
|
|||||||
}
|
}
|
||||||
private fun fadeOutStep() {
|
private fun fadeOutStep() {
|
||||||
// fade-out: linearly ramp fadeVolume to 0.0, then act according to Status (Playing->Silence/Pause/Shutdown)
|
// 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
|
fadeVolume -= fadeStep
|
||||||
|
try {
|
||||||
if (fadeVolume >= 0.001f && music != null && music!!.isPlaying) {
|
if (fadeVolume >= 0.001f && music != null && music!!.isPlaying) {
|
||||||
music!!.volume = volume * fadeVolume
|
music!!.volume = volume * fadeVolume
|
||||||
return
|
return
|
||||||
@ -107,6 +109,7 @@ class MusicTrackController(private var volume: Float) {
|
|||||||
fadeVolume = 0f
|
fadeVolume = 0f
|
||||||
music!!.volume = 0f
|
music!!.volume = 0f
|
||||||
music!!.pause()
|
music!!.pause()
|
||||||
|
} catch (_: Throwable) {}
|
||||||
state = State.Idle
|
state = State.Idle
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +124,19 @@ class MusicTrackController(private var volume: Float) {
|
|||||||
state = fade
|
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 */
|
/** @return [Music.isPlaying] (Gdx music stream is playing) unless [state] says it won't make sense */
|
||||||
fun isPlaying() = state.canPlay && music?.isPlaying == true
|
fun isPlaying() = state.canPlay && music?.isPlaying == true
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousSc
|
|||||||
}
|
}
|
||||||
|
|
||||||
addCloseButton {
|
addCloseButton {
|
||||||
|
previousScreen.game.musicController.onChange(null)
|
||||||
previousScreen.game.limitOrientationsHelper?.allowPortrait(settings.allowAndroidPortrait)
|
previousScreen.game.limitOrientationsHelper?.allowPortrait(settings.allowAndroidPortrait)
|
||||||
if (previousScreen is WorldScreen)
|
if (previousScreen is WorldScreen)
|
||||||
previousScreen.enableNextTurnButtonAfterOptions()
|
previousScreen.enableNextTurnButtonAfterOptions()
|
||||||
@ -210,6 +211,7 @@ class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousSc
|
|||||||
if (previousScreen.game.musicController.isMusicAvailable()) {
|
if (previousScreen.game.musicController.isMusicAvailable()) {
|
||||||
addMusicVolumeSlider()
|
addMusicVolumeSlider()
|
||||||
addMusicPauseSlider()
|
addMusicPauseSlider()
|
||||||
|
addMusicCurrentlyPlaying()
|
||||||
} else {
|
} else {
|
||||||
addDownloadMusic()
|
addDownloadMusic()
|
||||||
}
|
}
|
||||||
@ -456,6 +458,17 @@ class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousSc
|
|||||||
add(pauseLengthSlider).pad(5f).row()
|
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() {
|
private fun Table.addDownloadMusic() {
|
||||||
val downloadMusicButton = "Download music".toTextButton()
|
val downloadMusicButton = "Download music".toTextButton()
|
||||||
add(downloadMusicButton).colspan(2).row()
|
add(downloadMusicButton).colspan(2).row()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user