convolutions and some audio samples are on CommonResourcePool now

This commit is contained in:
minjaesong
2024-01-24 15:34:52 +09:00
parent 76f7b2a145
commit 89b372e4d8
6 changed files with 156 additions and 98 deletions

View File

@@ -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<ComplexArray> {
val id = "convolution$$module.$path"
if (CommonResourcePool.resourceExists(id)) {
val fft = CommonResourcePool.getAs<Array<ComplexArray>>(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<ComplexArray> {
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<FloatArray> {
val id = "audiosamplesf32$$module.$path"
if (CommonResourcePool.resourceExists(id)) {
return CommonResourcePool.getAs<Array<FloatArray>>(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<FloatArray> {
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<Int>("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
}
}

View File

@@ -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)

View File

@@ -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<FloatArray>
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<Int>("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))

View File

@@ -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
)

View File

@@ -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

View File

@@ -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
)
}
}