mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
individual fadein/out req for tracks
This commit is contained in:
@@ -1341,6 +1341,7 @@ public class App implements ApplicationListener {
|
||||
public static String customDir;
|
||||
/** defaultDir + "/Custom/Music" */
|
||||
public static String customMusicDir;
|
||||
public static String customAmbientDir;
|
||||
|
||||
private static void getDefaultDirectory() {
|
||||
String OS = OSName.toUpperCase();
|
||||
@@ -1376,6 +1377,7 @@ public class App implements ApplicationListener {
|
||||
importDir = defaultDir + "/Imports";
|
||||
customDir = defaultDir + "/Custom";
|
||||
customMusicDir = customDir + "/Music";
|
||||
customAmbientDir = customDir + "/Ambient";
|
||||
|
||||
System.out.println(String.format("os.name = %s (with identifier %s)", OSName, operationSystem));
|
||||
System.out.println(String.format("os.version = %s", OSVersion));
|
||||
|
||||
@@ -6,7 +6,7 @@ open class MusicGovernor {
|
||||
|
||||
}
|
||||
|
||||
protected var state = 0 // 0: disabled, 1: playing, 2: waiting
|
||||
protected var musicState = 0 // 0: disabled, 1: playing, 2: waiting
|
||||
protected var intermissionAkku = 0f
|
||||
protected var intermissionLength = 1f
|
||||
protected var musicFired = false
|
||||
|
||||
@@ -959,5 +959,5 @@ fun distBetween(a: ActorWithBody, bpos: Vector2): Double {
|
||||
val dist = min(min(bpos.distanceSquared(apos1), bpos.distanceSquared(apos2)), bpos.distanceSquared(apos3))
|
||||
return dist.sqrt()
|
||||
}
|
||||
|
||||
fun getHashStr(length: Int = 5) = (0 until length).map { "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"[Math.random().times(32).toInt()] }.joinToString("")
|
||||
const val hashStrMap = "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"
|
||||
fun getHashStr(length: Int = 5) = (0 until length).map { hashStrMap[Math.random().times(32).toInt()] }.joinToString("")
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RAT
|
||||
import net.torvald.terrarum.modulebasegame.MusicContainer
|
||||
import net.torvald.terrarum.tryDispose
|
||||
import java.lang.Thread.MAX_PRIORITY
|
||||
import java.util.*
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
@@ -47,10 +48,18 @@ object AudioMixer: Disposable {
|
||||
else if (it == 2) "SFX"
|
||||
else if (it == 3) "GUI"
|
||||
else if (it == 4) "BUS1"
|
||||
else "Trk${it+1}", isBus = (it == 4)
|
||||
else "Trk${it+1}", isBus = (it == 4), maxVolumeFun = {
|
||||
when (it) {
|
||||
0 -> { musicVolume }
|
||||
1 -> { ambientVolume }
|
||||
2 -> { sfxVolume }
|
||||
3 -> { guiVolume }
|
||||
else -> { 1.0 }
|
||||
}
|
||||
}
|
||||
) }
|
||||
|
||||
val masterTrack = TerrarumAudioMixerTrack("Master", true)
|
||||
val masterTrack = TerrarumAudioMixerTrack("Master", true) { masterVolume }
|
||||
|
||||
val musicTrack: TerrarumAudioMixerTrack
|
||||
get() = tracks[0]
|
||||
@@ -109,12 +118,21 @@ object AudioMixer: Disposable {
|
||||
}
|
||||
|
||||
|
||||
private var fadeAkku = 0.0
|
||||
private var fadeLength = DEFAULT_FADEOUT_LEN
|
||||
private var fadeoutFired = false
|
||||
private var fadeinFired = false
|
||||
private var fadeTarget = 0.0
|
||||
private var fadeStart = 0.0
|
||||
data class FadeRequest(
|
||||
var fadeAkku: Double = 0.0,
|
||||
var fadeLength: Double = DEFAULT_FADEOUT_LEN,
|
||||
var fadeoutFired: Boolean = false,
|
||||
var fadeinFired: Boolean = false,
|
||||
var fadeTarget: Double = 0.0,
|
||||
var fadeStart: Double = 0.0,
|
||||
)
|
||||
|
||||
private val fadeReqs = HashMap<TerrarumAudioMixerTrack, FadeRequest>().also { map ->
|
||||
listOf(musicTrack, ambientTrack, sfxMixTrack, guiTrack, fadeBus).forEach {
|
||||
map[it] = FadeRequest()
|
||||
}
|
||||
}
|
||||
private val fadeReqsCol = fadeReqs.entries
|
||||
|
||||
private var lpAkku = 0.0
|
||||
private var lpLength = 0.4
|
||||
@@ -138,34 +156,31 @@ object AudioMixer: Disposable {
|
||||
|
||||
|
||||
// process fades
|
||||
if (fadeoutFired) {
|
||||
fadeAkku += delta
|
||||
val step = fadeAkku / fadeLength
|
||||
fadeBus.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget)
|
||||
fadeReqsCol.forEach { val track = it.key; val req = it.value
|
||||
if (req.fadeoutFired) {
|
||||
req.fadeAkku += delta
|
||||
val step = req.fadeAkku / req.fadeLength
|
||||
track.volume = FastMath.interpolateLinear(step, req.fadeStart, req.fadeTarget)
|
||||
|
||||
if (fadeAkku >= fadeLength) {
|
||||
fadeoutFired = false
|
||||
fadeBus.volume = fadeTarget
|
||||
fadeBus.volume = fadeTarget
|
||||
if (req.fadeAkku >= req.fadeLength) {
|
||||
req.fadeoutFired = false
|
||||
track.volume = req.fadeTarget
|
||||
track.volume = req.fadeTarget
|
||||
|
||||
if (fadeTarget == 0.0) {
|
||||
musicTrack.currentTrack = null
|
||||
ambientTrack.currentTrack = null
|
||||
if (req.fadeTarget == 0.0) {
|
||||
track.currentTrack = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fadeinFired) {
|
||||
fadeAkku += delta
|
||||
val step = fadeAkku / fadeLength
|
||||
fadeBus.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget)
|
||||
else if (req.fadeinFired) {
|
||||
req.fadeAkku += delta
|
||||
val step = req.fadeAkku / req.fadeLength
|
||||
track.volume = FastMath.interpolateLinear(step, req.fadeStart, req.fadeTarget)
|
||||
|
||||
// if (musicTrack.isPlaying == false) {
|
||||
// musicTrack.play()
|
||||
// }
|
||||
|
||||
if (fadeAkku >= fadeLength) {
|
||||
fadeBus.volume = fadeTarget
|
||||
fadeinFired = false
|
||||
if (req.fadeAkku >= req.fadeLength) {
|
||||
track.volume = req.fadeTarget
|
||||
req.fadeinFired = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,32 +226,45 @@ object AudioMixer: Disposable {
|
||||
|
||||
fun startMusic(song: MusicContainer) {
|
||||
if (musicTrack.isPlaying == true) {
|
||||
requestFadeOut(DEFAULT_FADEOUT_LEN)
|
||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||
}
|
||||
musicTrack.nextTrack = song
|
||||
}
|
||||
|
||||
fun stopMusic() {
|
||||
requestFadeOut(DEFAULT_FADEOUT_LEN)
|
||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||
}
|
||||
|
||||
fun requestFadeOut(length: Double, target: Double = 0.0) {
|
||||
if (!fadeoutFired) {
|
||||
fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||
fadeAkku = 0.0
|
||||
fadeoutFired = true
|
||||
fadeTarget = target
|
||||
fadeStart = fadeBus.volume
|
||||
fun startAmb(song: MusicContainer) {
|
||||
if (ambientTrack.isPlaying == true) {
|
||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||
}
|
||||
ambientTrack.nextTrack = song
|
||||
}
|
||||
|
||||
fun stopAmb() {
|
||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||
}
|
||||
|
||||
fun requestFadeOut(track: TerrarumAudioMixerTrack, length: Double, target: Double = 0.0) {
|
||||
val req = fadeReqs[track]!!
|
||||
if (!req.fadeoutFired) {
|
||||
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||
req.fadeAkku = 0.0
|
||||
req.fadeoutFired = true
|
||||
req.fadeTarget = target * track.maxVolume
|
||||
req.fadeStart = fadeBus.volume
|
||||
}
|
||||
}
|
||||
|
||||
fun requestFadeIn(length: Double, target: Double = 1.0) {
|
||||
if (!fadeinFired) {
|
||||
fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||
fadeAkku = 0.0
|
||||
fadeinFired = true
|
||||
fadeTarget = target
|
||||
fadeStart = fadeBus.volume
|
||||
fun requestFadeIn(track: TerrarumAudioMixerTrack, length: Double, target: Double = 1.0) {
|
||||
val req = fadeReqs[track]!!
|
||||
if (!req.fadeinFired) {
|
||||
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||
req.fadeAkku = 0.0
|
||||
req.fadeinFired = true
|
||||
req.fadeTarget = target * track.maxVolume
|
||||
req.fadeStart = fadeBus.volume
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.Disposable
|
||||
import com.badlogic.gdx.utils.Queue
|
||||
import net.torvald.reflection.forceInvoke
|
||||
import net.torvald.terrarum.getHashStr
|
||||
import net.torvald.terrarum.hashStrMap
|
||||
import net.torvald.terrarum.modulebasegame.MusicContainer
|
||||
import java.lang.Thread.MAX_PRIORITY
|
||||
import kotlin.math.log10
|
||||
@@ -13,7 +14,7 @@ import kotlin.math.pow
|
||||
|
||||
typealias TrackVolume = Double
|
||||
|
||||
class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, val isBus: Boolean = false): Disposable {
|
||||
class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, val isBus: Boolean = false, private val maxVolumeFun: () -> Double): Disposable {
|
||||
|
||||
companion object {
|
||||
const val SAMPLING_RATE = 48000
|
||||
@@ -26,6 +27,9 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, v
|
||||
}
|
||||
|
||||
val hash = getHashStr()
|
||||
private val hashCode0 = hash.map { hashStrMap.indexOf(it) }.foldIndexed(0) { i, acc, c ->
|
||||
acc or (c shl (5*i))
|
||||
}
|
||||
|
||||
var currentTrack: MusicContainer? = null
|
||||
var nextTrack: MusicContainer? = null
|
||||
@@ -37,6 +41,9 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, v
|
||||
currentTrack?.gdxMusic?.volume = volume.toFloat()
|
||||
}
|
||||
|
||||
val maxVolume: Double
|
||||
get() = maxVolumeFun()
|
||||
|
||||
var pan = 0.0
|
||||
|
||||
var dBfs: Double
|
||||
@@ -152,6 +159,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, v
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun hashCode() = hashCode0
|
||||
}
|
||||
|
||||
fun fullscaleToDecibels(fs: Double) = 20.0 * log10(fs)
|
||||
|
||||
@@ -31,9 +31,8 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
MusicContainer(
|
||||
it.nameWithoutExtension.replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
||||
it,
|
||||
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath)),
|
||||
{ stopMusic() }
|
||||
)
|
||||
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath))
|
||||
) { stopMusic() }
|
||||
}
|
||||
catch (e: GdxRuntimeException) {
|
||||
e.printStackTrace()
|
||||
@@ -43,6 +42,25 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
|
||||
private var musicBin: ArrayList<Int> = ArrayList(songs.indices.toList().shuffled())
|
||||
|
||||
private val ambients: List<MusicContainer> =
|
||||
File(App.customAmbientDir).listFiles()?.mapNotNull {
|
||||
printdbg(this, "Ambient: ${it.absolutePath}")
|
||||
try {
|
||||
MusicContainer(
|
||||
it.nameWithoutExtension.replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
||||
it,
|
||||
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath))
|
||||
) { stopMusic() }
|
||||
}
|
||||
catch (e: GdxRuntimeException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
} ?: emptyList() // TODO test code
|
||||
|
||||
private var ambientsBin: ArrayList<Int> = ArrayList(ambients.indices.toList().shuffled())
|
||||
|
||||
|
||||
|
||||
init {
|
||||
songs.forEach {
|
||||
@@ -59,10 +77,11 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
private val STATE_PLAYING = 2
|
||||
private val STATE_INTERMISSION = 3
|
||||
|
||||
protected var ambState = 0
|
||||
protected var ambFired = false
|
||||
|
||||
private fun stopMusic() {
|
||||
// AudioManager.stopMusic() // music will stop itself; with this line not commented, the stop-callback from the already disposed musicgovernor will stop the music queued by the new musicgovernor instance
|
||||
state = STATE_INTERMISSION
|
||||
musicState = STATE_INTERMISSION
|
||||
intermissionAkku = 0f
|
||||
intermissionLength = 30f + 30f * Math.random().toFloat() // 30s-60s
|
||||
musicFired = false
|
||||
@@ -73,7 +92,23 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
AudioMixer.startMusic(song)
|
||||
printdbg(this, "Now playing: $song")
|
||||
INGAME.sendNotification("Now Playing $EMDASH ${song.name}")
|
||||
state = STATE_PLAYING
|
||||
musicState = STATE_PLAYING
|
||||
}
|
||||
|
||||
|
||||
private fun stopAmbient() {
|
||||
ambState = STATE_INTERMISSION
|
||||
intermissionAkku = 0f
|
||||
intermissionLength = 30f + 30f * Math.random().toFloat() // 30s-60s
|
||||
ambFired = false
|
||||
printdbg(this, "Intermission: $intermissionLength seconds")
|
||||
}
|
||||
|
||||
private fun startAmbient(song: MusicContainer) {
|
||||
AudioMixer.startAmb(song)
|
||||
printdbg(this, "Now playing: $song")
|
||||
INGAME.sendNotification("Now Playing $EMDASH ${song.name}")
|
||||
ambState = STATE_PLAYING
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +122,10 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
}
|
||||
|
||||
// val ingame = ingame as TerrarumIngame
|
||||
if (state == 0) state = STATE_INTERMISSION
|
||||
if (musicState == 0) musicState = STATE_INTERMISSION
|
||||
|
||||
|
||||
when (state) {
|
||||
when (musicState) {
|
||||
STATE_FIREPLAY -> {
|
||||
if (!musicFired) {
|
||||
musicFired = true
|
||||
@@ -112,15 +147,39 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
|
||||
if (intermissionAkku >= intermissionLength) {
|
||||
intermissionAkku = 0f
|
||||
state = 1
|
||||
musicState = STATE_FIREPLAY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (ambState) {
|
||||
STATE_FIREPLAY -> {
|
||||
if (!ambFired) {
|
||||
ambFired = true
|
||||
|
||||
val song = ambients[ambientsBin.removeAt(0)]
|
||||
// prevent same song to play twice
|
||||
if (ambientsBin.isEmpty()) {
|
||||
ambientsBin = ArrayList(ambients.indices.toList().shuffled())
|
||||
}
|
||||
|
||||
startAmbient(song)
|
||||
}
|
||||
}
|
||||
STATE_PLAYING -> {
|
||||
// stopMusic() will be called when the music finishes; it's on the setOnCompletionListener
|
||||
}
|
||||
STATE_INTERMISSION -> {
|
||||
ambState = STATE_FIREPLAY
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
AudioMixer.stopMusic() // explicit call for fade-out when the game instance quits
|
||||
AudioMixer.requestFadeOut(AudioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits
|
||||
stopMusic()
|
||||
stopAmbient()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ class UIInventoryFull(
|
||||
INGAME.setTooltipMessage(null)
|
||||
|
||||
AudioMixer.requestLowpassIn(0.25)
|
||||
AudioMixer.requestFadeOut(0.25, 0.5)
|
||||
AudioMixer.requestFadeOut(AudioMixer.fadeBus, 0.25, 0.5)
|
||||
}
|
||||
|
||||
override fun doClosing(delta: Float) {
|
||||
@@ -376,7 +376,7 @@ class UIInventoryFull(
|
||||
INGAME.setTooltipMessage(null)
|
||||
|
||||
AudioMixer.requestLowpassOut(0.25)
|
||||
AudioMixer.requestFadeIn(0.25, 1.0)
|
||||
AudioMixer.requestFadeIn(AudioMixer.fadeBus, 0.25, 1.0)
|
||||
}
|
||||
|
||||
override fun endOpening(delta: Float) {
|
||||
|
||||
Reference in New Issue
Block a user