From 29c4d925424c2538d7064f3cdbcff7d6f7615a1f Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 26 Nov 2023 19:00:29 +0900 Subject: [PATCH] parallelised audio processing --- src/net/torvald/terrarum/audio/AudioMixer.kt | 33 ++++++++++++++++++- .../terrarum/audio/TerrarumAudioFilter.kt | 2 +- .../terrarum/audio/TerrarumAudioMixerTrack.kt | 2 +- .../terrarum/ui/BasicDebugInfoWindow.kt | 24 +++++++++++++- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/net/torvald/terrarum/audio/AudioMixer.kt b/src/net/torvald/terrarum/audio/AudioMixer.kt index 5b3dea110..fb2a85ed0 100644 --- a/src/net/torvald/terrarum/audio/AudioMixer.kt +++ b/src/net/torvald/terrarum/audio/AudioMixer.kt @@ -9,10 +9,12 @@ import net.torvald.terrarum.App import net.torvald.terrarum.ModMgr import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED +import net.torvald.terrarum.concurrent.ThreadExecutor import net.torvald.terrarum.modulebasegame.MusicContainer import net.torvald.terrarum.tryDispose import java.lang.Thread.MAX_PRIORITY import java.util.* +import java.util.concurrent.Callable import kotlin.math.* /** @@ -86,8 +88,9 @@ object AudioMixer: Disposable { var processing = true + private val processingExecutor = ThreadExecutor() val processingThread = Thread { - while (processing) { + /*while (processing) { // process tracks.forEach { if (!it.processor.paused) { @@ -100,12 +103,32 @@ object AudioMixer: Disposable { Thread.sleep(1) }*/ + while (!masterTrack.pcmQueue.isEmpty) { + masterTrack.adev!!.writeSamples(masterTrack.pcmQueue.removeFirst()) // it blocks until the queue is consumed + } + }*/ + + while (processing) { + parallelProcessingSchedule.forEach { tracks -> + val callables = tracks.map { Callable { + if (!it.processor.paused) { + it.processor.run() + } + } } + + processingExecutor.renew() + processingExecutor.submitAll(callables) + processingExecutor.join() + } + while (!masterTrack.pcmQueue.isEmpty) { masterTrack.adev!!.writeSamples(masterTrack.pcmQueue.removeFirst()) // it blocks until the queue is consumed } } } + val parallelProcessingSchedule: Array> + // val feeder = FeedSamplesToAdev(BUFFER_SIZE, SAMPLING_RATE, masterTrack) // val feedingThread = Thread(feeder) @@ -149,6 +172,14 @@ object AudioMixer: Disposable { masterTrack.addSidechainInput(guiTrack, 1.0) + parallelProcessingSchedule = arrayOf( + arrayOf(musicTrack, ambientTrack, sfxMixTrack, guiTrack), + arrayOf(sumBus, convolveBusOpen, convolveBusCave), + arrayOf(fadeBus), + arrayOf(masterTrack) + ) + + processingThread.priority = MAX_PRIORITY // higher = more predictable; audio delay is very noticeable so it gets high priority processingThread.start() // feedingThread.priority = MAX_PRIORITY diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt index 9e9898d1d..97117253d 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioFilter.kt @@ -312,7 +312,7 @@ class Reverb(val delayMS: Float = 36f, var feedback: Float = 0.92f, var lowpass: class Convolv(ir: File, val gain: Float = 1f / 256f): TerrarumAudioFilter() { - private val fftLen: Int + val fftLen: Int private val convFFT: Array // private val convFFTpartd: Array> // index: Channel, partition, frequencies private val inbuf: Array diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt index dee69c3d3..03e35a6b9 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt @@ -20,7 +20,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, v const val SAMPLING_RATE = 48000 const val SAMPLING_RATEF = 48000f const val SAMPLING_RATED = 48000.0 - const val BUFFER_SIZE = 512*4 // n ms -> 384 * n + const val BUFFER_SIZE = 256*4 // n ms -> 384 * n } val hash = getHashStr() diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index 429604208..b14df1e5c 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -398,6 +398,10 @@ class BasicDebugInfoWindow : UICanvas() { private val COL_METER_GRAD2 = Color(0x25a0f2_aa) private val COL_SENDS_GRAD = Color(0x50751c_aa) private val COL_SENDS_GRAD2 = Color(0xa0f225_aa.toInt()) + private val COL_METER_GRAD_YELLOW = Color(0x62471c_aa) + private val COL_METER_GRAD2_YELLOW = Color(0xc68f24_aa.toInt()) + private val COL_METER_GRAD_RED = Color(0x921c34_aa.toInt()) + private val COL_METER_GRAD2_RED = Color(0xfa687d_aa.toInt()) private val COL_METER_BAR = Color(0x4caee5_aa) private val COL_METER_BAR_OVER = Color(0xef8297_aa.toInt()) private val COL_METER_COMP_BAR = Color(0xf3d458_aa.toInt()) @@ -603,7 +607,7 @@ class BasicDebugInfoWindow : UICanvas() { "Highpass" to 16, "Buffer" to 16, "BinoPan" to 32, - "Convolv" to 16, + "Convolv" to 32, "Gain" to 16, "Scope" to stripW, ) @@ -653,8 +657,26 @@ class BasicDebugInfoWindow : UICanvas() { App.fontSmallNumbers.draw(batch, "Bs:${BUFFER_SIZE/4}", x+3f, y+1f) } is Convolv -> { + // processing speed bar + val w = filter.processingSpeed + val perc = w.coerceAtMost(2f) / 2f + batch.color = if (w > 1.5f) COL_METER_GRAD2 else if (w > 1f) COL_METER_GRAD2_YELLOW else COL_METER_GRAD2_RED + Toolkit.fillArea(batch, x.toFloat(), y.toFloat(), stripW * perc, 14f) + batch.color = if (w > 1.5f) COL_METER_GRAD else if (w > 1f) COL_METER_GRAD_YELLOW else COL_METER_GRAD_RED + Toolkit.fillArea(batch, x.toFloat(), y+14f, stripW * perc, 2f) + + // filter length bar + val g = FastMath.intLog2(BUFFER_SIZE / 4) + val perc2 = (FastMath.intLog2(filter.fftLen).minus(g).toFloat() / (16f - g)).coerceIn(0f, 1f) + batch.color = COL_METER_GRAD2 + Toolkit.fillArea(batch, x.toFloat(), y + 16f, stripW * perc2, 14f) + batch.color = COL_METER_GRAD + Toolkit.fillArea(batch, x.toFloat(), y + 16f+14f, stripW * perc2, 2f) + + // texts batch.color = FILTER_NAME_ACTIVE App.fontSmallNumbers.draw(batch, "P:${filter.processingSpeed.times(100).roundToInt().div(100f)}x", x+3f, y+1f) + App.fontSmallNumbers.draw(batch, "L:${filter.fftLen}", x+3f, y+17f) } is Gain -> { batch.color = FILTER_NAME_ACTIVE