From dc2f58d7540e4db49a2a2a6030d9d5758c774410 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 18 Nov 2023 14:17:54 +0900 Subject: [PATCH] lowpass in/out on opening menu --- src/net/torvald/terrarum/audio/AudioMixer.kt | 84 +++++++++++++++---- .../terrarum/audio/MixerTrackProcessor.kt | 6 +- .../terrarum/audio/TerrarumAudioFilter.kt | 18 +++- .../terrarum/audio/TerrarumAudioMixerTrack.kt | 4 +- .../terrarum/modulebasegame/BuildingMaker.kt | 4 + .../terrarum/modulebasegame/TerrarumIngame.kt | 5 ++ .../terrarum/modulebasegame/TitleScreen.kt | 6 ++ .../modulebasegame/ui/UIInventoryFull.kt | 9 ++ 8 files changed, 115 insertions(+), 21 deletions(-) diff --git a/src/net/torvald/terrarum/audio/AudioMixer.kt b/src/net/torvald/terrarum/audio/AudioMixer.kt index a3050d6b1..5cf5879a5 100644 --- a/src/net/torvald/terrarum/audio/AudioMixer.kt +++ b/src/net/torvald/terrarum/audio/AudioMixer.kt @@ -3,9 +3,15 @@ package net.torvald.terrarum.audio import com.badlogic.gdx.Gdx import com.badlogic.gdx.backends.lwjgl3.audio.Lwjgl3Audio import com.badlogic.gdx.utils.Disposable +import com.jme3.math.FastMath import net.torvald.terrarum.App +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATEF import net.torvald.terrarum.modulebasegame.MusicContainer import net.torvald.terrarum.tryDispose +import kotlin.math.log +import kotlin.math.log10 +import kotlin.math.pow /** * Any audio reference fed into this manager will get lost; you must manually store and dispose of them on your own. @@ -28,24 +34,36 @@ object AudioMixer: Disposable { get() = (App.getConfigDouble("sfxvolume") * App.getConfigDouble("mastervolume")) - private val tracks = Array(10) { TerrarumAudioMixerTrack("Audio Track #${it+1}") } + val tracks = Array(10) { TerrarumAudioMixerTrack( + if (it == 0) "BGM Track" + else if (it == 1) "AMB Track" + else "Audio Track #${it+1}" + ) } - private val masterTrack = TerrarumAudioMixerTrack("Master", true).also { master -> + val masterTrack = TerrarumAudioMixerTrack("Master", true).also { master -> tracks.forEach { master.addSidechainInput(it, 1.0) } - master.filters[0] = Lowpass(48000, TerrarumAudioMixerTrack.SAMPLING_RATE) } - private val musicTrack: TerrarumAudioMixerTrack + val musicTrack: TerrarumAudioMixerTrack get() = tracks[0] - private val ambientTrack: TerrarumAudioMixerTrack + val ambientTrack: TerrarumAudioMixerTrack get() = tracks[1] + init { + musicTrack.filters[0] = Lowpass(48000f, TerrarumAudioMixerTrack.SAMPLING_RATE) + ambientTrack.filters[0] = Lowpass(48000f, TerrarumAudioMixerTrack.SAMPLING_RATE) + } + private var fadeAkku = 0.0 private var fadeLength = DEFAULT_FADEOUT_LEN - private var fadeoutFired = false private var fadeinFired = false + private var lpAkku = 0.0 + private var lpLength = 0.4 + private var lpOutFired = false + private var lpInFired = false + // TODO make sidechaining work // TODO master volume controls the master track // TODO fadein/out controls the master track @@ -58,34 +76,51 @@ object AudioMixer: Disposable { fadeAkku += delta musicTrack.volume = (musicVolume * (1.0 - (fadeAkku / fadeLength))).coerceIn(0.0, 1.0) -// printdbg(this, "Fadeout fired - akku: $fadeAkku; volume: ${currentMusic?.gdxMusic?.volume}") - if (fadeAkku >= fadeLength) { fadeoutFired = false musicTrack.volume = 0.0 -// currentMusic?.gdxMusic?.pause() musicTrack.currentTrack = null - -// printdbg(this, "Fadeout end") } } - // process fadein request else if (fadeinFired) { fadeAkku += delta musicTrack.volume = (musicVolume * (fadeAkku / fadeLength)).coerceIn(0.0, 1.0) -// printdbg(this, "Fadein fired - akku: $fadeAkku; volume: ${currentMusic?.gdxMusic?.volume}") - if (musicTrack.isPlaying == false) { musicTrack.play() -// printdbg(this, "Fadein starting music ${currentMusic?.name}") } if (fadeAkku >= fadeLength) { musicTrack.volume = musicVolume fadeinFired = false + } + } -// printdbg(this, "Fadein end") + + if (lpOutFired) { + lpAkku += delta + val x = (lpAkku / lpLength).coerceIn(0.0, 1.0) + val q = 400.0 + val step = (q.pow(x) - 1) / (q - 1) // https://www.desmos.com/calculator/sttaq2qhzm + val cutoff = FastMath.interpolateLinear(step, SAMPLING_RATED / 100.0, SAMPLING_RATED) + (musicTrack.filters[0] as Lowpass).setCutoff(cutoff) + + if (lpAkku >= lpLength) { + lpOutFired = false + (musicTrack.filters[0] as Lowpass).setCutoff(SAMPLING_RATEF) + } + } + else if (lpInFired) { + lpAkku += delta + val x = (lpAkku / lpLength).coerceIn(0.0, 1.0) + val q = 400.0 + val step = log((q-1) * x + 1.0, q) // https://www.desmos.com/calculator/sttaq2qhzm + val cutoff = FastMath.interpolateLinear(step, SAMPLING_RATED, SAMPLING_RATED / 100.0) + (musicTrack.filters[0] as Lowpass).setCutoff(cutoff) + + if (lpAkku >= lpLength) { + (musicTrack.filters[0] as Lowpass).setCutoff(SAMPLING_RATEF / 100.0) + lpInFired = false } } @@ -126,6 +161,23 @@ object AudioMixer: Disposable { } + fun requestLowpassOut(length: Double) { + if (!lpOutFired) { + lpLength = length.coerceAtLeast(1.0/1024.0) + lpAkku = 0.0 + lpOutFired = true + } + } + + fun requestLowpassIn(length: Double) { + if (!lpInFired) { + lpLength = length.coerceAtLeast(1.0/1024.0) + lpAkku = 0.0 + lpInFired = true + } + } + + override fun dispose() { tracks.forEach { it.tryDispose() } masterTrack.tryDispose() diff --git a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt index c5a91ef54..ca76db009 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -9,6 +9,10 @@ import kotlin.math.absoluteValue * Created by minjaesong on 2023-11-17. */ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: TerrarumAudioMixerTrack): Runnable { + + val BACK_BUF_COUNT = 2 + + @Volatile private var running = true @Volatile private var paused = false private val pauseLock = java.lang.Object() @@ -163,7 +167,7 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru if (samplesL0 != null && samplesL1 != null && samplesR0 != null && samplesR1 != null) { // spin until queue is sufficiently empty - while (track.pcmQueue.size >= 4 && running) { + while (track.pcmQueue.size >= BACK_BUF_COUNT && running) { Thread.sleep(1) } diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt index 5e339036a..a00f63654 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt @@ -24,15 +24,27 @@ object NullFilter: TerrarumAudioFilter() { } -class Lowpass(cutoff: Int, rate: Int): TerrarumAudioFilter() { +class Lowpass(cutoff: Float, val rate: Int): TerrarumAudioFilter() { - val alpha: Float + private var alpha: Float = 0f init { - val RC: Float = 1f / (cutoff.toFloat() * FastMath.TWO_PI) + setCutoff(cutoff) + } + + fun setCutoff(cutoff: Float) { +// println("LP Cutoff: $cutoff") + val RC: Float = 1f / (cutoff * FastMath.TWO_PI) val dt: Float = 1f / rate alpha = dt / (RC + dt) } + fun setCutoff(cutoff: Double) { +// println("LP Cutoff: $cutoff") + val RC: Double = 1.0 / (cutoff * Math.PI * 2.0) + val dt: Double = 1.0 / rate + alpha = (dt / (RC + dt)).toFloat() + } + override fun thru(inbuf0: List, inbuf1: List, outbuf0: List, outbuf1: List) { for (ch in outbuf1.indices) { val out = outbuf1[ch] diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt index 70aea352d..0d99d6c9d 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt @@ -16,6 +16,8 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): companion object { const val SAMPLING_RATE = 48000 + const val SAMPLING_RATEF = 48000f + const val SAMPLING_RATED = 48000.0 } val hash = getHashStr() @@ -123,7 +125,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): // 1st ring of the hell: the THREADING HELL // - val BUFFER_SIZE = 16384 + val BUFFER_SIZE = 6000 internal var processor = MixerTrackProcessor(BUFFER_SIZE, SAMPLING_RATE, this) private val processorThread = Thread(processor).also { diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index 6caa310e3..e975e9a1f 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -7,6 +7,9 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion import net.torvald.terrarum.* import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE +import net.torvald.terrarum.audio.AudioMixer +import net.torvald.terrarum.audio.Lowpass +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.BlockPropUtil import net.torvald.terrarum.gameactors.* @@ -299,6 +302,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) { override fun show() { Gdx.input.inputProcessor = BuildingMakerController(this) + (AudioMixer.musicTrack.filters[0] as Lowpass).setCutoff(TerrarumAudioMixerTrack.SAMPLING_RATEF) super.show() } diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 7e3adbe0a..d0bd6ae69 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -12,6 +12,9 @@ import net.torvald.terrarum.Terrarum.getPlayerSaveFiledesc import net.torvald.terrarum.Terrarum.getWorldSaveFiledesc import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED +import net.torvald.terrarum.audio.AudioMixer +import net.torvald.terrarum.audio.Lowpass +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack import net.torvald.terrarum.blockproperties.BlockPropUtil import net.torvald.terrarum.blockstats.MinimapComposer import net.torvald.terrarum.blockstats.TileSurvey @@ -288,6 +291,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { IngameRenderer.setRenderedWorld(world) blockMarkingActor.isVisible = true + (AudioMixer.musicTrack.filters[0] as Lowpass).setCutoff(TerrarumAudioMixerTrack.SAMPLING_RATEF) + super.show() // this function sets gameInitialised = true } diff --git a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt index b9c914568..c209b3e74 100644 --- a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt @@ -17,6 +17,9 @@ import net.torvald.terrarum.App.printdbgerr import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF +import net.torvald.terrarum.audio.AudioMixer +import net.torvald.terrarum.audio.Lowpass +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack import net.torvald.terrarum.clut.Skybox import net.torvald.terrarum.console.CommandDict import net.torvald.terrarum.gameactors.* @@ -293,6 +296,9 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) { UILoadGovernor.reset() + (AudioMixer.musicTrack.filters[0] as Lowpass).setCutoff(TerrarumAudioMixerTrack.SAMPLING_RATEF) + + loadThingsWhileIntroIsVisible() printdbg(this, "show() exit") } diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt index a6eadaf3b..95d2d4a63 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt @@ -7,6 +7,11 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.jme3.math.FastMath import net.torvald.terrarum.* import net.torvald.terrarum.App.* +import net.torvald.terrarum.audio.AudioMixer +import net.torvald.terrarum.audio.Lowpass +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATEF import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.ui.Toolkit @@ -15,6 +20,7 @@ import net.torvald.terrarum.ui.UIHandler import net.torvald.terrarum.ui.UIItemHorizontalFadeSlide import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.unicode.* +import kotlin.math.pow /** * Created by minjaesong on 2017-10-21. @@ -358,6 +364,8 @@ class UIInventoryFull( transitionPanel.uis.forEach { it.opacity = FastMath.pow(opacity, 0.5f) } INGAME.pause() INGAME.setTooltipMessage(null) + + AudioMixer.requestLowpassIn(0.4) } override fun doClosing(delta: Float) { @@ -366,6 +374,7 @@ class UIInventoryFull( INGAME.resume() INGAME.setTooltipMessage(null) + AudioMixer.requestLowpassOut(0.4) } override fun endOpening(delta: Float) {