From 5c5f526d163e3650cbeb77d314d5378ae980aecd Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 18 Nov 2023 18:31:25 +0900 Subject: [PATCH] mixer overlay wip --- src/net/torvald/terrarum/audio/AudioMixer.kt | 1 + .../terrarum/audio/MixerTrackProcessor.kt | 6 +- .../terrarum/audio/TerrarumAudioFilter.kt | 20 ++- .../terrarum/audio/TerrarumAudioMixerTrack.kt | 3 +- .../modulebasegame/console/Inventory.kt | 1 + .../terrarum/ui/BasicDebugInfoWindow.kt | 122 +++++++++++++++++- 6 files changed, 145 insertions(+), 8 deletions(-) diff --git a/src/net/torvald/terrarum/audio/AudioMixer.kt b/src/net/torvald/terrarum/audio/AudioMixer.kt index 5cf5879a5..0602e2463 100644 --- a/src/net/torvald/terrarum/audio/AudioMixer.kt +++ b/src/net/torvald/terrarum/audio/AudioMixer.kt @@ -42,6 +42,7 @@ object AudioMixer: Disposable { val masterTrack = TerrarumAudioMixerTrack("Master", true).also { master -> tracks.forEach { master.addSidechainInput(it, 1.0) } + master.filters[0] = Buffer } val musicTrack: TerrarumAudioMixerTrack diff --git a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt index ca76db009..5c4c923d1 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -155,11 +155,15 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru } } } + + fout1.map { it.maxOf { it.absoluteValue } }.forEachIndexed { index, fl -> + maxSigLevel[index] = fl.toDouble() + } } + // by this time, the output buffer is filled with processed results, pause the execution if (!track.isMaster) { - fout1.map { it.maxBy { it.absoluteValue } }.forEachIndexed { index, fl -> maxSigLevel[index] = fl.toDouble() } this.pause() } diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt index a00f63654..789c83fe9 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt @@ -15,7 +15,7 @@ abstract class TerrarumAudioFilter { } } -object NullFilter: TerrarumAudioFilter() { +object NullFilter : TerrarumAudioFilter() { override fun thru(inbuf0: List, inbuf1: List, outbuf0: List, outbuf1: List) { outbuf1.forEachIndexed { index, outTrack -> System.arraycopy(inbuf1[index], 0, outTrack, 0, outTrack.size) @@ -24,11 +24,13 @@ object NullFilter: TerrarumAudioFilter() { } -class Lowpass(cutoff: Float, val rate: Int): TerrarumAudioFilter() { +class Lowpass(cutoff0: Float, val rate: Int): TerrarumAudioFilter() { + var cutoff = cutoff0.toDouble(); private set private var alpha: Float = 0f + init { - setCutoff(cutoff) + setCutoff(cutoff0) } fun setCutoff(cutoff: Float) { @@ -36,6 +38,7 @@ class Lowpass(cutoff: Float, val rate: Int): TerrarumAudioFilter() { val RC: Float = 1f / (cutoff * FastMath.TWO_PI) val dt: Float = 1f / rate alpha = dt / (RC + dt) + this.cutoff = cutoff.toDouble() } fun setCutoff(cutoff: Double) { @@ -43,6 +46,7 @@ class Lowpass(cutoff: Float, val rate: Int): TerrarumAudioFilter() { val RC: Double = 1.0 / (cutoff * Math.PI * 2.0) val dt: Double = 1.0 / rate alpha = (dt / (RC + dt)).toFloat() + this.cutoff = cutoff } override fun thru(inbuf0: List, inbuf1: List, outbuf0: List, outbuf1: List) { @@ -59,3 +63,13 @@ class Lowpass(cutoff: Float, val rate: Int): TerrarumAudioFilter() { } } + +object Buffer : TerrarumAudioFilter() { + init { + bypass = true + } + + override fun thru(inbuf0: List, inbuf1: List, outbuf0: List, outbuf1: List) { + bypass = true + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt index 0d99d6c9d..3c8ab1cd1 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt @@ -18,6 +18,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): const val SAMPLING_RATE = 48000 const val SAMPLING_RATEF = 48000f const val SAMPLING_RATED = 48000.0 + const val BUFFER_SIZE = 8000 } val hash = getHashStr() @@ -125,8 +126,6 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): // 1st ring of the hell: the THREADING HELL // - val BUFFER_SIZE = 6000 - internal var processor = MixerTrackProcessor(BUFFER_SIZE, SAMPLING_RATE, this) private val processorThread = Thread(processor).also { it.start() diff --git a/src/net/torvald/terrarum/modulebasegame/console/Inventory.kt b/src/net/torvald/terrarum/modulebasegame/console/Inventory.kt index bd6d85c9b..9c3029051 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/Inventory.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/Inventory.kt @@ -37,6 +37,7 @@ internal object Inventory : ConsoleCommand { "remove" -> if (args.size > 3) removeItem(actor, args[2], args[3].toLong()) else removeItem(actor, args[2]) "equip" -> equipItem(actor, args[2]) "unequip"-> unequipItem(actor, args[2]) + "clear" -> actor.inventory.clear() else -> printUsage() } } diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index 9da91ab0d..738cce0bd 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -11,6 +11,8 @@ import net.torvald.terrarum.* import net.torvald.terrarum.Terrarum.mouseTileX import net.torvald.terrarum.Terrarum.mouseTileY import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE +import net.torvald.terrarum.audio.* +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.BUFFER_SIZE import net.torvald.terrarum.controller.TerrarumController import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.fmod @@ -70,9 +72,11 @@ class BasicDebugInfoWindow : UICanvas() { private val KEY_TIMERS = Input.Keys.T // + CONTROL_LEFT private val KEY_WEATHERS = Input.Keys.W // + CONTROL_LEFT + private val KEY_AUDIOMIXER = Input.Keys.M // + CONTROL_LEFT private var showTimers = false private var showWeatherInfo = false + private var showAudioMixer = false override fun show() { ingame = Terrarum.ingame @@ -138,12 +142,13 @@ class BasicDebugInfoWindow : UICanvas() { // toggle show-something showTimers = showTimers xor (Gdx.input.isKeyJustPressed(KEY_TIMERS) && Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) showWeatherInfo = showWeatherInfo xor (Gdx.input.isKeyJustPressed(KEY_WEATHERS) && Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) + showAudioMixer = showAudioMixer xor (Gdx.input.isKeyJustPressed(KEY_AUDIOMIXER) && Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) drawMain(batch) if (showTimers) drawTimers(batch) if (showWeatherInfo) drawWeatherInfo(batch) - + if (showAudioMixer) drawAudioMixer(batch) } @@ -349,7 +354,6 @@ class BasicDebugInfoWindow : UICanvas() { } } - private fun drawWeatherInfo(batch: SpriteBatch) { val weatherbox = INGAME.world.weatherbox val drawX = App.scr.width - 170 @@ -369,6 +373,120 @@ class BasicDebugInfoWindow : UICanvas() { } } + private val stripW = 64 + private val stripGap = 1 + private val stripFilterHeight = 32 + private val stripFaderHeight = 200 + private val stripH = stripFaderHeight + stripFilterHeight * 4 + + private val COL_WELL = Color(0x374854_aa) + private val COL_WELL2 = Color(0x3f5360_aa) + private val COL_WELL3 = Color(0x485437_aa) + private val COL_FILTER_TITLE = Color(0x72777d_aa) + private val COL_FILTER_TITLE_SHADE = Color(0x505558_aa) + private val COL_FILTER_WELL_BACK = Color(0x222325_aa) + private val COL_MIXER_BACK = Color(0x0f110c_aa) + private val COL_METER_TROUGH = Color(0x242527_aa) + private val COL_METER_GRAD = Color(0x1c5075_aa) + private val COL_METER_GRAD2 = Color(0x2ca3f3_aa) + private val COL_METER_BAR = Color(0x76c9fb_aa) + + private val FILTER_NAME_ACTIVE = Color(0xeeeeee_bf.toInt()) + private val FILTER_BYPASSED = Color(0xf1b934_bf.toInt()) + private val trackBack = listOf(COL_WELL, COL_WELL2) + private val meterTroughHeight = 16 * 11 + 5 + private val meterHeight = meterTroughHeight - 4 + private fun drawAudioMixer(batch: SpriteBatch) { + + val x = App.scr.width - 186 - (AudioMixer.tracks.size + 1) * (stripW + stripGap) + val y = App.scr.height - 38 - stripH + + val strips = AudioMixer.tracks + AudioMixer.masterTrack + +// batch.color = COL_MIXER_BACK +// Toolkit.fillArea(batch, x - stripGap, y - stripGap, strips.size * (stripW + stripGap) + stripGap, stripH + 2*stripGap) + + strips.forEachIndexed { index, track -> drawStrip(batch, x + index * (stripW + stripGap), y, track, index) } + } + + private fun drawStrip(batch: SpriteBatch, x: Int, y: Int, track: TerrarumAudioMixerTrack, index: Int) { + // back + batch.color = if (track.isMaster) COL_WELL3 else trackBack[index % 2] + Toolkit.fillArea(batch, x, y, stripW, stripH) + + // filterbank back + batch.color = COL_FILTER_WELL_BACK + Toolkit.fillArea(batch, x, y, stripW, stripFilterHeight * 4) + + track.filters.forEachIndexed { i, filter -> if (filter !is NullFilter) { + // draw filter title back + batch.color = COL_FILTER_TITLE_SHADE + Toolkit.fillArea(batch, x, y + stripFilterHeight * i, stripW, 16) + batch.color = COL_FILTER_TITLE + Toolkit.fillArea(batch, x, y + stripFilterHeight * i, stripW, 14) + // draw filter name + batch.color = if (filter.bypass) FILTER_BYPASSED else FILTER_NAME_ACTIVE + App.fontSmallNumbers.draw(batch, filter.javaClass.simpleName, x + 3f, y + stripFilterHeight * i + 1f) + + drawFilterParam(batch, x, y + stripFilterHeight * i + 16, filter, track) + } } + + val faderY = y + stripFilterHeight * 4 + + // fader + val dB = track.dBfs + val dBstr = dB.toIntAndFrac(2,1) + val dBfs = dB.coerceIn(-96.0, 0.0).plus(96.0).div(96.0) + batch.color = FILTER_NAME_ACTIVE + App.fontSmallNumbers.draw(batch, dBstr, x+3f, faderY+1f) + + // fader trough + batch.color = COL_METER_TROUGH + Toolkit.fillArea(batch, x+16, faderY+16, 19, meterTroughHeight) + for (i in 0..16) { + val y = faderY + 18 + i * 11 + val x = x + if (i == 0 || i == 16) 16 else 17 + val w = if (i == 0 || i == 16) 19 else 17 + batch.color = if (i == 0 || i == 16) COL_METER_GRAD2 else COL_METER_GRAD + Toolkit.fillArea(batch, x, y, w, 1) + } + + // fader labels + batch.color = FILTER_NAME_ACTIVE + for (i in 0..16 step 2) { + val y = faderY + 11f + i * 11 + val s = (i*6).toString().padStart(2, ' ') + App.fontSmallNumbers.draw(batch, s, x + 1f, y) + } + + // fader + batch.color = COL_METER_BAR + for (ch in 0..1) { + val fs = track.processor.maxSigLevel[ch] + val dBfs = fullscaleToDecibels(fs) + + val x = x + 19f + 7 * ch + val h = ((dBfs + 96.0) / 96.0 * -meterHeight).coerceAtMost(0.0).toFloat() + Toolkit.fillArea(batch, x, faderY + 18f + meterHeight, 6f, h) + } + + } + + private fun drawFilterParam(batch: SpriteBatch, x: Int, y: Int, filter: TerrarumAudioFilter, track: TerrarumAudioMixerTrack) { + when (filter) { + is Lowpass -> { + batch.color = FILTER_NAME_ACTIVE + App.fontSmallNumbers.draw(batch, "F:${filter.cutoff.toInt()}", x+3f, y+1f) + } + is Buffer -> { + batch.color = FILTER_NAME_ACTIVE + App.fontSmallNumbers.draw(batch, "Buf:${track.pcmQueue.size}", x+3f, y+1f) + App.fontSmallNumbers.draw(batch, "Bs:${BUFFER_SIZE/4}", x+3f, y+17f) + } + } + } + + private val colHairline = Color(0xf22100ff.toInt()) private val colGraph = Toolkit.Theme.COL_SELECTED private val colGrapi = Toolkit.Theme.COL_SELECTED.cpy().mul(0.5f, 0.5f, 0.5f, 1f)