diff --git a/src/net/torvald/terrarum/audio/AudioHelper.kt b/src/net/torvald/terrarum/audio/AudioHelper.kt new file mode 100644 index 000000000..1dffc654a --- /dev/null +++ b/src/net/torvald/terrarum/audio/AudioHelper.kt @@ -0,0 +1,120 @@ +package net.torvald.terrarum.audio + +import com.badlogic.gdx.Gdx +import com.jme3.math.FastMath +import net.torvald.reflection.forceInvoke +import net.torvald.terrarum.CommonResourcePool +import net.torvald.terrarum.ModMgr +import net.torvald.terrarum.serialise.toUint +import java.io.File + +/** + * Created by minjaesong on 2024-01-24. + */ +object AudioHelper { + + fun getIR(module: String, path: String): Array { + val id = "convolution$$module.$path" + + if (CommonResourcePool.resourceExists(id)) { + val fft = CommonResourcePool.getAs>(id) + + return fft + } + else { + val ir = ModMgr.getFile(module, path) + val fft = createIR(ir) + + CommonResourcePool.addToLoadingList(id) { fft } + CommonResourcePool.loadAll() + + return fft + } + } + + private fun createIR(ir: File): Array { + if (!ir.exists()) { + throw IllegalArgumentException("Impulse Response file '${ir.path}' does not exist.") + } + + val sampleCount = (ir.length().toInt() / 8)//.coerceAtMost(65536) + val fftLen = FastMath.nextPowerOfTwo(sampleCount) + + println("IR '${ir.path}' Sample Count = $sampleCount; FFT Length = $fftLen") + + val conv = Array(2) { FloatArray(fftLen) } + + ir.inputStream().let { + for (i in 0 until sampleCount) { + val f1 = Float.fromBits(it.read().and(255) or + it.read().and(255).shl(8) or + it.read().and(255).shl(16) or + it.read().and(255).shl(24)) + val f2 = Float.fromBits(it.read().and(255) or + it.read().and(255).shl(8) or + it.read().and(255).shl(16) or + it.read().and(255).shl(24)) + conv[0][i] = f1 + conv[1][i] = f2 + } + + it.close() + } + + // fourier-transform the 'conv' + return Array(2) { FFT.fft(conv[it]) } + } + + fun getAudioInSamples(module: String, path: String): Array { + val id = "audiosamplesf32$$module.$path" + + if (CommonResourcePool.resourceExists(id)) { + return CommonResourcePool.getAs>(id) + } + else { + val file = ModMgr.getFile(module, path) + val samples = createAudioInSamples(file) + + CommonResourcePool.addToLoadingList(id) { samples } + CommonResourcePool.loadAll() + + return samples + } + } + + private fun createAudioInSamples(static: File): Array { + val music = Gdx.audio.newMusic(Gdx.files.absolute(static.absolutePath)) + val readbuf = ByteArray(AudioProcessBuf.MP3_CHUNK_SIZE * 4) + val OUTBUF_BLOCK_SIZE_IN_BYTES = (48000 * 60) * 2 * 2 + var outbuf = ByteArray(OUTBUF_BLOCK_SIZE_IN_BYTES) + var bytesRead = 0 + + fun expandOutbuf() { + val newOutBuf = ByteArray(outbuf.size + OUTBUF_BLOCK_SIZE_IN_BYTES) + System.arraycopy(outbuf, 0, newOutBuf, 0, outbuf.size) + outbuf = newOutBuf + } + + while (true) { + val readSize = music.forceInvoke("read", arrayOf(readbuf))!! + if (readSize <= 0) break + + // check if outbuf has room + if (bytesRead + readSize > outbuf.size) expandOutbuf() + + // actually copy the bytes + System.arraycopy(readbuf, 0, outbuf, bytesRead, readSize) + + bytesRead += readSize + } + + // convert bytes to float samples + val staticSample = arrayOf(FloatArray(bytesRead / 4), FloatArray(bytesRead / 4)) + for (i in staticSample[0].indices) { + staticSample[0][i] = (outbuf[4*i+0].toUint() or outbuf[4*i+1].toUint().shl(8)).toShort() / 32767f + staticSample[1][i] = (outbuf[4*i+2].toUint() or outbuf[4*i+3].toUint().shl(8)).toShort() / 32767f + } + return staticSample + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/audio/dsp/Convolv.kt b/src/net/torvald/terrarum/audio/dsp/Convolv.kt index cc46dee2b..16c1bf8e5 100644 --- a/src/net/torvald/terrarum/audio/dsp/Convolv.kt +++ b/src/net/torvald/terrarum/audio/dsp/Convolv.kt @@ -24,7 +24,7 @@ import kotlin.math.roundToInt * * Created by minjaesong on 2023-11-25. */ -class Convolv(ir: File, val crossfeed: Float, gain: Float = 1f / 256f): TerrarumAudioFilter() { +class Convolv(irModule: String, irPath: String, val crossfeed: Float, gain: Float = 1f / 256f): TerrarumAudioFilter() { private val gain: Float = gain / (1f + crossfeed) @@ -35,39 +35,9 @@ class Convolv(ir: File, val crossfeed: Float, gain: Float = 1f / 256f): Terrarum var processingSpeed = 1f; private set init { - if (!ir.exists()) { - throw IllegalArgumentException("Impulse Response file '${ir.path}' does not exist.") - } - - val sampleCount = (ir.length().toInt() / 8)//.coerceAtMost(65536) - fftLen = FastMath.nextPowerOfTwo(sampleCount) - - println("IR '${ir.path}' Sample Count = $sampleCount; FFT Length = $fftLen") - - val conv = Array(2) { FloatArray(fftLen) } + convFFT = AudioHelper.getIR(irModule, irPath) + fftLen = convFFT[0].size sumbuf = Array(2) { ComplexArray(FloatArray(fftLen * 2)) } - - ir.inputStream().let { - for (i in 0 until sampleCount) { - val f1 = Float.fromBits(it.read().and(255) or - it.read().and(255).shl(8) or - it.read().and(255).shl(16) or - it.read().and(255).shl(24)) - val f2 = Float.fromBits(it.read().and(255) or - it.read().and(255).shl(8) or - it.read().and(255).shl(16) or - it.read().and(255).shl(24)) - conv[0][i] = f1 - conv[1][i] = f2 - } - - it.close() - } - - // fourier-transform the 'conv' - convFFT = Array(2) { - FFT.fft(conv[it]) - } } private var realtime = (App.audioBufferSize / TerrarumAudioMixerTrack.SAMPLING_RATEF * 1000000000L) diff --git a/src/net/torvald/terrarum/audio/dsp/LoFi.kt b/src/net/torvald/terrarum/audio/dsp/LoFi.kt index 92637f09a..f5bf2ba46 100644 --- a/src/net/torvald/terrarum/audio/dsp/LoFi.kt +++ b/src/net/torvald/terrarum/audio/dsp/LoFi.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.g2d.SpriteBatch import net.torvald.reflection.forceInvoke import net.torvald.terrarum.App +import net.torvald.terrarum.audio.AudioHelper import net.torvald.terrarum.audio.AudioProcessBuf.Companion.MP3_CHUNK_SIZE import net.torvald.terrarum.serialise.toUint import java.io.File @@ -19,48 +20,17 @@ import kotlin.math.tanh * * Created by minjaesong on 2024-01-21. */ -open class LoFi(static: File, ir: File, val crossfeed: Float, gain: Float = 1f / 256f): TerrarumAudioFilter(), DspCompressor { +open class LoFi( + staticModule: String, staticPath: String, + irModule: String, irPath: String, + val crossfeed: Float, gain: Float = 1f / 256f +): TerrarumAudioFilter(), DspCompressor { override val downForce = arrayOf(1.0f, 1.0f) - internal val staticSample: List + internal val staticSample = AudioHelper.getAudioInSamples(staticModule, staticPath) private var staticSamplePlayCursor = 0 - init { - val music = Gdx.audio.newMusic(Gdx.files.absolute(static.absolutePath)) - val readbuf = ByteArray(MP3_CHUNK_SIZE * 4) - val OUTBUF_BLOCK_SIZE_IN_BYTES = (48000 * 60) * 2 * 2 - var outbuf = ByteArray(OUTBUF_BLOCK_SIZE_IN_BYTES) - var bytesRead = 0 - - fun expandOutbuf() { - val newOutBuf = ByteArray(outbuf.size + OUTBUF_BLOCK_SIZE_IN_BYTES) - System.arraycopy(outbuf, 0, newOutBuf, 0, outbuf.size) - outbuf = newOutBuf - } - - while (true) { - val readSize = music.forceInvoke("read", arrayOf(readbuf))!! - if (readSize <= 0) break - - // check if outbuf has room - if (bytesRead + readSize > outbuf.size) expandOutbuf() - - // actually copy the bytes - System.arraycopy(readbuf, 0, outbuf, bytesRead, readSize) - - bytesRead += readSize - } - - // convert bytes to float samples - staticSample = listOf(FloatArray(bytesRead / 4), FloatArray(bytesRead / 4)) - for (i in staticSample[0].indices) { - staticSample[0][i] = (outbuf[4*i+0].toUint() or outbuf[4*i+1].toUint().shl(8)).toShort() / 32767f - staticSample[1][i] = (outbuf[4*i+2].toUint() or outbuf[4*i+3].toUint().shl(8)).toShort() / 32767f - } - - } - - internal val convolver = Convolv(ir, crossfeed, gain) + internal val convolver = Convolv(irModule, irPath, crossfeed, gain) private val immAfterStaticMix = listOf(FloatArray(App.audioBufferSize), FloatArray(App.audioBufferSize)) private val immAfterConvolv = listOf(FloatArray(App.audioBufferSize), FloatArray(App.audioBufferSize)) diff --git a/src/net/torvald/terrarum/audio/dsp/Phono.kt b/src/net/torvald/terrarum/audio/dsp/Phono.kt index c2aa4a938..6b39e5ef7 100644 --- a/src/net/torvald/terrarum/audio/dsp/Phono.kt +++ b/src/net/torvald/terrarum/audio/dsp/Phono.kt @@ -1,16 +1,15 @@ package net.torvald.terrarum.audio.dsp import net.torvald.terrarum.ModMgr -import java.io.File /** * Crackle and pops of the phonographs. * * Created by minjaesong on 2024-01-24. */ -class Phono(ir: File, crossfeed: Float, gain: Float) : LoFi( - ModMgr.getFile("basegame", "audio/effects/static/phono_pops.ogg"), - ir, crossfeed, gain +class Phono(irModule: String, irPath: String, crossfeed: Float, gain: Float) : LoFi( + "basegame", "audio/effects/static/phono_pops.ogg", + irModule, irPath, crossfeed, gain ) /** @@ -18,9 +17,9 @@ class Phono(ir: File, crossfeed: Float, gain: Float) : LoFi( * * Created by minjaesong on 2024-01-24. */ -class Tape(ir: File, crossfeed: Float, gain: Float) : LoFi( - ModMgr.getFile("basegame", "audio/effects/static/tape_hiss.ogg"), - ir, crossfeed, gain +class Tape(irModule: String, irPath: String, crossfeed: Float, gain: Float) : LoFi( + "basegame", "audio/effects/static/tape_hiss.ogg", + irModule, irPath, crossfeed, gain ) /** @@ -30,7 +29,7 @@ class Tape(ir: File, crossfeed: Float, gain: Float) : LoFi( * * Created by minjaesong on 2024-01-24. */ -class Holo(ir: File, crossfeed: Float, gain: Float) : LoFi( - ModMgr.getFile("basegame", "audio/effects/static/film_pops.ogg"), - ir, crossfeed, gain +class Holo(irModule: String, irPath: String, crossfeed: Float, gain: Float) : LoFi( + "basegame", "audio/effects/static/film_pops.ogg", + irModule, irPath, crossfeed, gain ) \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt b/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt index 90a832603..57ed8bbdd 100644 --- a/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/FancyWorldReadLoadScreen.kt @@ -17,19 +17,19 @@ import kotlin.math.sqrt open class FancyWorldReadLoadScreen(screenToBeLoaded: IngameInstance, private val worldwidth: Int, private val worldheight: Int, override var preLoadJob: (LoadScreenBase) -> Unit) : LoadScreenBase() { init { - CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer01") { + CommonResourcePool.addToLoadingList("basegame.gui/loadscrlayer01") { Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer01.png")) } - CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer02") { + CommonResourcePool.addToLoadingList("basegame.gui/loadscrlayer02") { Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer02.png")) } - CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer03") { + CommonResourcePool.addToLoadingList("basegame.gui/loadscrlayer03") { Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer03.png")) } - CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer04") { + CommonResourcePool.addToLoadingList("basegame.gui/loadscrlayer04") { Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer04.png")) } - CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer05") { + CommonResourcePool.addToLoadingList("basegame.gui/loadscrlayer05") { Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer05.png")) } CommonResourcePool.loadAll() @@ -51,14 +51,14 @@ open class FancyWorldReadLoadScreen(screenToBeLoaded: IngameInstance, private va val xoff = (Math.random() * (1024-764)/2).toInt() val baseTileTex = arrayOf( - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer01"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer02"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer04"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), - CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer01"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer02"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer03"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer04"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer05"), + CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer05"), ) val drawWidth = Toolkit.drawWidth diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureJukebox.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureJukebox.kt index 32e295703..e8e37ebaf 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureJukebox.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureJukebox.kt @@ -119,10 +119,9 @@ class FixtureJukebox : Electric, PlaysMusic { App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2f) { startAudio(musicNowPlaying!!) { it.filters[filterIndex] = Phono( - ModMgr.getFile( - "basegame", - "audio/convolution/Soundwoofer - large_speaker_Marshall JVM 205C SM57 A 0 0 1.bin" - ), 0f, 5f / 16f + "basegame", + "audio/convolution/Soundwoofer - large_speaker_Marshall JVM 205C SM57 A 0 0 1.bin", + 0f, 5f / 16f ) } }