From 468476f41eecccd21bb88ff0951a901bb40a2cdb Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 20 Nov 2023 03:15:43 +0900 Subject: [PATCH] audioengine: serial processing --- .../torvald/terrarum/AudioManagerRunnable.kt | 2 +- src/net/torvald/terrarum/audio/AudioMixer.kt | 96 ++++++++++++++----- .../terrarum/audio/MixerTrackProcessor.kt | 53 ++++++---- .../terrarum/audio/TerrarumAudioFilter.kt | 1 + .../terrarum/audio/TerrarumAudioMixerTrack.kt | 16 ++-- .../terrarum/modulebasegame/BuildingMaker.kt | 2 +- .../terrarum/modulebasegame/TerrarumIngame.kt | 2 +- .../terrarum/modulebasegame/TitleScreen.kt | 2 +- .../modulebasegame/ui/UIInventoryFull.kt | 8 +- .../terrarum/ui/BasicDebugInfoWindow.kt | 3 +- 10 files changed, 125 insertions(+), 60 deletions(-) diff --git a/src/net/torvald/terrarum/AudioManagerRunnable.kt b/src/net/torvald/terrarum/AudioManagerRunnable.kt index 3bfe1fcd6..9999fae82 100644 --- a/src/net/torvald/terrarum/AudioManagerRunnable.kt +++ b/src/net/torvald/terrarum/AudioManagerRunnable.kt @@ -18,7 +18,7 @@ class AudioManagerRunnable : Runnable { oldT = T; AudioMixer.update(dT) // println("AudioManagerRunnable dT = ${dT * 1000f} ms") - Thread.sleep(30L) + Thread.sleep(1L) } catch (e: InterruptedException) { break diff --git a/src/net/torvald/terrarum/audio/AudioMixer.kt b/src/net/torvald/terrarum/audio/AudioMixer.kt index a49c90d7b..96b7a8a23 100644 --- a/src/net/torvald/terrarum/audio/AudioMixer.kt +++ b/src/net/torvald/terrarum/audio/AudioMixer.kt @@ -5,6 +5,8 @@ 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.MixerTrackProcessor.Companion.BACK_BUF_COUNT +import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.BUFFER_SIZE import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.INDEX_AMB import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.INDEX_BGM import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE @@ -12,6 +14,7 @@ import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RAT import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATEF import net.torvald.terrarum.modulebasegame.MusicContainer import net.torvald.terrarum.tryDispose +import java.lang.Thread.MAX_PRIORITY import kotlin.math.* /** @@ -35,19 +38,16 @@ object AudioMixer: Disposable { get() = App.getConfigDouble("sfxvolume") - val tracks = Array(4) { TerrarumAudioMixerTrack( + val tracks = Array(5) { TerrarumAudioMixerTrack( if (it == 0) "BGM" else if (it == 1) "AMB" else if (it == 2) "Sfx Mix" else if (it == 3) "GUI" - else "Trk${it+1}" + else if (it == 4) "BUS1" + else "Trk${it+1}", isBus = (it == 4) ) } - val masterTrack = TerrarumAudioMixerTrack("Master", true).also { master -> - master.volume = masterVolume - master.filters[0] = Buffer - tracks.forEachIndexed { i, it -> master.addSidechainInput(it, if (i == INDEX_BGM) musicVolume else if (i == INDEX_AMB) ambientVolume else 1.0) } - } + val masterTrack = TerrarumAudioMixerTrack("Master", true) val musicTrack: TerrarumAudioMixerTrack get() = tracks[0] @@ -58,14 +58,54 @@ object AudioMixer: Disposable { val guiTrack: TerrarumAudioMixerTrack get() = tracks[3] + val fadeBus: TerrarumAudioMixerTrack + get() = tracks[4] - init { - musicTrack.filters[0] = Lowpass(SAMPLING_RATE / 2f, SAMPLING_RATE) - ambientTrack.filters[0] = Lowpass(SAMPLING_RATE / 2f, SAMPLING_RATE) - sfxMixTrack.filters[0] = Lowpass(SAMPLING_RATE / 2f, SAMPLING_RATE) + var processing = true + + val processingThread = Thread { + while (processing) { + // process + tracks.forEach { + if (!it.processor.paused) { + it.processor.run() + } + } + masterTrack.processor.run() + + /*while (masterTrack.pcmQueue.size >= BACK_BUF_COUNT && masterTrack.processor.running && processing) { + Thread.sleep(1) + }*/ + + while (!masterTrack.pcmQueue.isEmpty) { + masterTrack.adev!!.writeSamples(masterTrack.pcmQueue.removeFirst()) // it blocks until the queue is consumed + } + } + } + +// val feeder = FeedSamplesToAdev(BUFFER_SIZE, SAMPLING_RATE, masterTrack) +// val feedingThread = Thread(feeder) + + + init { + masterTrack.volume = masterVolume + masterTrack.filters[0] = Buffer + + fadeBus.addSidechainInput(musicTrack, 1.0) + fadeBus.addSidechainInput(ambientTrack, 1.0) + fadeBus.addSidechainInput(sfxMixTrack, 1.0) + fadeBus.filters[0] = Lowpass(SAMPLING_RATE / 2f, SAMPLING_RATE) + + masterTrack.addSidechainInput(fadeBus, 1.0) + masterTrack.addSidechainInput(guiTrack, 1.0) + + + processingThread.priority = MAX_PRIORITY // higher = more predictable; audio delay is very noticeable so it gets high priority + processingThread.start() +// feedingThread.priority = MAX_PRIORITY +// feedingThread.start() } - val faderTrack = arrayOf(musicTrack, ambientTrack, sfxMixTrack) private var fadeAkku = 0.0 private var fadeLength = DEFAULT_FADEOUT_LEN @@ -92,12 +132,12 @@ object AudioMixer: Disposable { if (fadeoutFired) { fadeAkku += delta val step = fadeAkku / fadeLength - faderTrack.forEach { it.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget) } + fadeBus.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget) if (fadeAkku >= fadeLength) { fadeoutFired = false - musicTrack.volume = fadeTarget - faderTrack.forEach { it.volume = fadeTarget } + fadeBus.volume = fadeTarget + fadeBus.volume = fadeTarget if (fadeTarget == 0.0) { musicTrack.currentTrack = null @@ -108,14 +148,14 @@ object AudioMixer: Disposable { else if (fadeinFired) { fadeAkku += delta val step = fadeAkku / fadeLength - faderTrack.forEach { it.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget) } + fadeBus.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget) // if (musicTrack.isPlaying == false) { // musicTrack.play() // } if (fadeAkku >= fadeLength) { - faderTrack.forEach { it.volume = fadeTarget } + fadeBus.volume = fadeTarget fadeinFired = false } } @@ -128,12 +168,12 @@ object AudioMixer: Disposable { val b = ln(lpStart / lpTarget) / -1.0 val a = lpStart val cutoff = a * exp(b * t) - faderTrack.forEach { (it.filters[0] as Lowpass).setCutoff(cutoff) } + (fadeBus.filters[0] as Lowpass).setCutoff(cutoff) if (lpAkku >= lpLength) { lpOutFired = false - faderTrack.forEach { (it.filters[0] as Lowpass).setCutoff(lpTarget) } + (fadeBus.filters[0] as Lowpass).setCutoff(lpTarget) } } else if (lpInFired) { @@ -143,10 +183,10 @@ object AudioMixer: Disposable { val b = ln(lpStart / lpTarget) / -1.0 val a = lpStart val cutoff = a * exp(b * t) - faderTrack.forEach { (it.filters[0] as Lowpass).setCutoff(cutoff) } + (fadeBus.filters[0] as Lowpass).setCutoff(cutoff) if (lpAkku >= lpLength) { - faderTrack.forEach { (it.filters[0] as Lowpass).setCutoff(lpTarget) } + (fadeBus.filters[0] as Lowpass).setCutoff(lpTarget) lpInFired = false } } @@ -155,7 +195,7 @@ object AudioMixer: Disposable { if (musicTrack.isPlaying != true && musicTrack.nextTrack != null) { // printdbg(this, "Playing next music: ${nextMusic!!.name}") musicTrack.queueNext(null) - musicTrack.volume = 1.0 + fadeBus.volume = 1.0 musicTrack.play() } } @@ -177,7 +217,7 @@ object AudioMixer: Disposable { fadeAkku = 0.0 fadeoutFired = true fadeTarget = target - fadeStart = musicTrack.volume + fadeStart = fadeBus.volume } } @@ -187,7 +227,7 @@ object AudioMixer: Disposable { fadeAkku = 0.0 fadeinFired = true fadeTarget = target - fadeStart = musicTrack.volume + fadeStart = fadeBus.volume } } @@ -197,7 +237,7 @@ object AudioMixer: Disposable { lpLength = length.coerceAtLeast(1.0/1024.0) lpAkku = 0.0 lpOutFired = true - lpStart = (musicTrack.filters[0] as Lowpass).cutoff + lpStart = (fadeBus.filters[0] as Lowpass).cutoff lpTarget = SAMPLING_RATED / 2.0 } } @@ -207,13 +247,17 @@ object AudioMixer: Disposable { lpLength = length.coerceAtLeast(1.0/1024.0) lpAkku = 0.0 lpInFired = true - lpStart = (musicTrack.filters[0] as Lowpass).cutoff + lpStart = (fadeBus.filters[0] as Lowpass).cutoff lpTarget = SAMPLING_RATED / 100.0 } } override fun dispose() { + processing = false + processingThread.join() +// feeder.stop() +// feedingThread.join() 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 52eb1762d..7b037f134 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -4,7 +4,6 @@ import com.badlogic.gdx.utils.Queue import net.torvald.reflection.forceInvoke import net.torvald.terrarum.audio.AudioMixer.masterVolume import net.torvald.terrarum.audio.AudioMixer.musicVolume -import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE import kotlin.math.absoluteValue /** @@ -16,8 +15,8 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru val BACK_BUF_COUNT = 1 } - @Volatile private var running = true - @Volatile private var paused = false + @Volatile var running = true; private set + @Volatile var paused = false; private set private val pauseLock = java.lang.Object() @@ -39,8 +38,8 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru if (true) println("[AudioAdapter ${track.name}] $msg") } override fun run() { - while (running) { - synchronized(pauseLock) { +// while (running) { // uncomment to multithread + /*synchronized(pauseLock) { // uncomment to multithread if (!running) { // may have changed while waiting to // synchronize on pauseLock breakBomb = true @@ -64,11 +63,11 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru } } - if (breakBomb) break + if (breakBomb) break*/ // uncomment to multithread // Your code here // fetch deviceBufferSize amount of sample from the disk - if (!track.isMaster && track.streamPlaying) { + if (!track.isMaster && !track.isBus && track.streamPlaying) { streamBuf.fetchBytes { val bytesRead = track.currentTrack?.gdxMusic?.forceInvoke("read", arrayOf(it)) if (bytesRead == null || bytesRead <= 0) { // some class (namely Mp3) may return 0 instead of negative value @@ -77,6 +76,7 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru track.streamPlaying = false track.fireSongFinishHook() } + } } @@ -93,7 +93,7 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru var bufEmpty = false - if (track.isMaster) { + if (track.isMaster || track.isBus) { // TEST CODE must combine all the inputs track.sidechainInputs[TerrarumAudioMixerTrack.INDEX_BGM]?.let { samplesL0 = it.first.processor.fout0[0].applyVolume(musicVolume) @@ -137,7 +137,7 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru samplesR1 = streamBuf.getR1(track.volume) } - if (samplesL0 != null && samplesL1 != null && samplesR0 != null && samplesR1 != null) { + if (samplesL0 != null /*&& samplesL1 != null && samplesR0 != null && samplesR1 != null*/) { // run the input through the stack of filters val filterStack = track.filters.filter { !it.bypass && it !is NullFilter } @@ -188,12 +188,12 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru this.pause() } else { - if (samplesL0 != null && samplesL1 != null && samplesR0 != null && samplesR1 != null) { + if (samplesL0 != null /*&& samplesL1 != null && samplesR0 != null && samplesR1 != null*/) { // spin until queue is sufficiently empty - while (track.pcmQueue.size >= BACK_BUF_COUNT && running) { + /*while (track.pcmQueue.size >= BACK_BUF_COUNT && running) { // uncomment to multithread Thread.sleep(1) - } + }*/ // printdbg("PUSHE; Queue size: ${track.pcmQueue.size}") val masvol = masterVolume @@ -202,17 +202,30 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru } // spin - Thread.sleep((bufferSize / 8L / rate).coerceAtLeast(1L)) +// Thread.sleep(((1000*bufferSize) / 8L / rate).coerceAtLeast(1L)) // uncomment to multithread // wake sidechain processors - track.getSidechains().forEach { - if (it?.processor?.running == true) - it?.processor?.resume() + resumeSidechainsRecursively(track, track.name) + } +// } // uncomment to multithread + } + + + private fun resumeSidechainsRecursively(track: TerrarumAudioMixerTrack?, caller: String) { + track?.getSidechains()?.forEach { + if (it?.processor?.running == true) { + it.processor.resume() + it.getSidechains().forEach { + if (it?.processor?.running == true) { + it.processor.resume() + resumeSidechainsRecursively(it, caller + caller) + } } } } } + fun stop() { running = false // you might also want to interrupt() the Thread that is @@ -222,11 +235,13 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru } fun pause() { +// printdbg("PAUSE") // you may want to throw an IllegalStateException if !running paused = true } fun resume() { +// printdbg("RESUME") synchronized(pauseLock) { paused = false pauseLock.notifyAll() // Unblocks thread @@ -252,6 +267,10 @@ class FeedSamplesToAdev(val bufferSize: Int, val rate: Int, val track: TerrarumA if (!track.isMaster) throw IllegalArgumentException("Track is not master") } + val sleepTime = (1000000000.0 * ((bufferSize / 4.0) / TerrarumAudioMixerTrack.SAMPLING_RATED)).toLong() + val sleepMS = sleepTime / 1000000 + val sleepNS = (sleepTime % 1000000).toInt() + private fun printdbg(msg: Any) { if (true) println("[AudioAdapter ${track.name}] $msg") } @@ -270,7 +289,7 @@ class FeedSamplesToAdev(val bufferSize: Int, val rate: Int, val track: TerrarumA // printdbg("QUEUE EMPTY QUEUE EMPTY QUEUE EMPTY ") // } - Thread.sleep((bufferSize / 8L / rate).coerceAtLeast(1L)) +// Thread.sleep(sleepMS, sleepNS) } } diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt index 4e72f295e..dcd717227 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt @@ -50,6 +50,7 @@ class Lowpass(cutoff0: Float, val rate: Int): TerrarumAudioFilter() { } override fun thru(inbuf0: List, inbuf1: List, outbuf0: List, outbuf1: List) { + // fixme crackles on buffer-edge for (ch in outbuf1.indices) { val out = outbuf1[ch] val inn = inbuf1[ch] diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt index 663af3a05..b54d878c6 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt @@ -13,13 +13,13 @@ import kotlin.math.pow typealias TrackVolume = Double -class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): Disposable { +class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, val isBus: Boolean = false): Disposable { companion object { const val SAMPLING_RATE = 48000 const val SAMPLING_RATEF = 48000f const val SAMPLING_RATED = 48000.0 - const val BUFFER_SIZE = 6000 + const val BUFFER_SIZE = 512 // n ms -> 384 * n const val INDEX_BGM = 0 const val INDEX_AMB = 1 @@ -112,12 +112,12 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): get() = currentTrack?.gdxMusic?.isPlaying override fun dispose() { - if (isMaster) { + /*if (isMaster) { // uncomment to multithread queueDispatcher.stop() queueDispatcherThread.join() } processor.stop() - processorThread.join() + processorThread.join()*/ adev?.dispose() } @@ -131,10 +131,10 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): // 1st ring of the hell: the THREADING HELL // internal var processor = MixerTrackProcessor(BUFFER_SIZE, SAMPLING_RATE, this) - private val processorThread = Thread(processor).also { + /*private val processorThread = Thread(processor).also { // uncomment to multithread it.priority = MAX_PRIORITY // higher = more predictable; audio delay is very noticeable so it gets high priority it.start() - } + }*/ internal var pcmQueue = Queue>() private lateinit var queueDispatcher: FeedSamplesToAdev private lateinit var queueDispatcherThread: Thread @@ -143,13 +143,13 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false): pcmQueue.addLast(listOf(FloatArray(BUFFER_SIZE / 4), FloatArray(BUFFER_SIZE / 4))) pcmQueue.addLast(listOf(FloatArray(BUFFER_SIZE / 4), FloatArray(BUFFER_SIZE / 4))) - if (isMaster) { + /*if (isMaster) { // uncomment to multithread queueDispatcher = FeedSamplesToAdev(BUFFER_SIZE, SAMPLING_RATE, this) queueDispatcherThread = Thread(queueDispatcher).also { it.priority = MAX_PRIORITY // higher = more predictable; audio delay is very noticeable so it gets high priority it.start() } - } + }*/ } } diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index 3fa351df9..eed956915 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -302,7 +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 / 2) + (AudioMixer.fadeBus.filters[0] as Lowpass).setCutoff(TerrarumAudioMixerTrack.SAMPLING_RATEF / 2) super.show() } diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 233219be5..6ce9a9234 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -292,7 +292,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { IngameRenderer.setRenderedWorld(world) blockMarkingActor.isVisible = true - AudioMixer.faderTrack.forEach { (it.filters[0] as Lowpass).setCutoff(SAMPLING_RATEF / 2) } + (AudioMixer.fadeBus.filters[0] as Lowpass).setCutoff(SAMPLING_RATEF / 2) 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 886a40364..7bb12a27a 100644 --- a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt @@ -296,7 +296,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) { UILoadGovernor.reset() - (AudioMixer.musicTrack.filters[0] as Lowpass).setCutoff(TerrarumAudioMixerTrack.SAMPLING_RATEF / 2) + (AudioMixer.fadeBus.filters[0] as Lowpass).setCutoff(TerrarumAudioMixerTrack.SAMPLING_RATEF / 2) loadThingsWhileIntroIsVisible() diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt index 0f3a300f8..e6e745787 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt @@ -365,8 +365,8 @@ class UIInventoryFull( INGAME.pause() INGAME.setTooltipMessage(null) - AudioMixer.requestLowpassIn(0.3) - AudioMixer.requestFadeOut(0.3, 0.5) + AudioMixer.requestLowpassIn(0.25) + AudioMixer.requestFadeOut(0.25, 0.5) } override fun doClosing(delta: Float) { @@ -375,8 +375,8 @@ class UIInventoryFull( INGAME.resume() INGAME.setTooltipMessage(null) - AudioMixer.requestLowpassOut(0.3) - AudioMixer.requestFadeIn(0.3, 1.0) + AudioMixer.requestLowpassOut(0.25) + AudioMixer.requestFadeIn(0.25, 1.0) } override fun endOpening(delta: Float) { diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index 2759879d7..8a27d500f 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -382,6 +382,7 @@ class BasicDebugInfoWindow : UICanvas() { private val COL_WELL = Color(0x374854_aa) private val COL_WELL2 = Color(0x3f5360_aa) private val COL_WELL3 = Color(0x485437_aa) + private val COL_WELL4 = Color(0x543748_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) @@ -425,7 +426,7 @@ class BasicDebugInfoWindow : UICanvas() { 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] + batch.color = if (track.isMaster) COL_WELL4 else if (track.isBus) COL_WELL3 else trackBack[index % 2] Toolkit.fillArea(batch, x, y, stripW, stripH) // strip/name separator