mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-14 07:36:06 +09:00
convolutions and some audio samples are on CommonResourcePool now
This commit is contained in:
120
src/net/torvald/terrarum/audio/AudioHelper.kt
Normal file
120
src/net/torvald/terrarum/audio/AudioHelper.kt
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ import kotlin.math.roundToInt
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2023-11-25.
|
* 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)
|
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
|
var processingSpeed = 1f; private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!ir.exists()) {
|
convFFT = AudioHelper.getIR(irModule, irPath)
|
||||||
throw IllegalArgumentException("Impulse Response file '${ir.path}' does not exist.")
|
fftLen = convFFT[0].size
|
||||||
}
|
|
||||||
|
|
||||||
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) }
|
|
||||||
sumbuf = Array(2) { ComplexArray(FloatArray(fftLen * 2)) }
|
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)
|
private var realtime = (App.audioBufferSize / TerrarumAudioMixerTrack.SAMPLING_RATEF * 1000000000L)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx
|
|||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
import net.torvald.reflection.forceInvoke
|
import net.torvald.reflection.forceInvoke
|
||||||
import net.torvald.terrarum.App
|
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.audio.AudioProcessBuf.Companion.MP3_CHUNK_SIZE
|
||||||
import net.torvald.terrarum.serialise.toUint
|
import net.torvald.terrarum.serialise.toUint
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -19,48 +20,17 @@ import kotlin.math.tanh
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2024-01-21.
|
* 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)
|
override val downForce = arrayOf(1.0f, 1.0f)
|
||||||
|
|
||||||
internal val staticSample: List<FloatArray>
|
internal val staticSample = AudioHelper.getAudioInSamples(staticModule, staticPath)
|
||||||
private var staticSamplePlayCursor = 0
|
private var staticSamplePlayCursor = 0
|
||||||
|
|
||||||
init {
|
internal val convolver = Convolv(irModule, irPath, crossfeed, gain)
|
||||||
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)
|
|
||||||
|
|
||||||
private val immAfterStaticMix = listOf(FloatArray(App.audioBufferSize), FloatArray(App.audioBufferSize))
|
private val immAfterStaticMix = listOf(FloatArray(App.audioBufferSize), FloatArray(App.audioBufferSize))
|
||||||
private val immAfterConvolv = listOf(FloatArray(App.audioBufferSize), FloatArray(App.audioBufferSize))
|
private val immAfterConvolv = listOf(FloatArray(App.audioBufferSize), FloatArray(App.audioBufferSize))
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package net.torvald.terrarum.audio.dsp
|
package net.torvald.terrarum.audio.dsp
|
||||||
|
|
||||||
import net.torvald.terrarum.ModMgr
|
import net.torvald.terrarum.ModMgr
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crackle and pops of the phonographs.
|
* Crackle and pops of the phonographs.
|
||||||
*
|
*
|
||||||
* Created by minjaesong on 2024-01-24.
|
* Created by minjaesong on 2024-01-24.
|
||||||
*/
|
*/
|
||||||
class Phono(ir: File, crossfeed: Float, gain: Float) : LoFi(
|
class Phono(irModule: String, irPath: String, crossfeed: Float, gain: Float) : LoFi(
|
||||||
ModMgr.getFile("basegame", "audio/effects/static/phono_pops.ogg"),
|
"basegame", "audio/effects/static/phono_pops.ogg",
|
||||||
ir, crossfeed, gain
|
irModule, irPath, crossfeed, gain
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,9 +17,9 @@ class Phono(ir: File, crossfeed: Float, gain: Float) : LoFi(
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2024-01-24.
|
* Created by minjaesong on 2024-01-24.
|
||||||
*/
|
*/
|
||||||
class Tape(ir: File, crossfeed: Float, gain: Float) : LoFi(
|
class Tape(irModule: String, irPath: String, crossfeed: Float, gain: Float) : LoFi(
|
||||||
ModMgr.getFile("basegame", "audio/effects/static/tape_hiss.ogg"),
|
"basegame", "audio/effects/static/tape_hiss.ogg",
|
||||||
ir, crossfeed, gain
|
irModule, irPath, crossfeed, gain
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +29,7 @@ class Tape(ir: File, crossfeed: Float, gain: Float) : LoFi(
|
|||||||
*
|
*
|
||||||
* Created by minjaesong on 2024-01-24.
|
* Created by minjaesong on 2024-01-24.
|
||||||
*/
|
*/
|
||||||
class Holo(ir: File, crossfeed: Float, gain: Float) : LoFi(
|
class Holo(irModule: String, irPath: String, crossfeed: Float, gain: Float) : LoFi(
|
||||||
ModMgr.getFile("basegame", "audio/effects/static/film_pops.ogg"),
|
"basegame", "audio/effects/static/film_pops.ogg",
|
||||||
ir, crossfeed, gain
|
irModule, irPath, crossfeed, gain
|
||||||
)
|
)
|
||||||
@@ -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() {
|
open class FancyWorldReadLoadScreen(screenToBeLoaded: IngameInstance, private val worldwidth: Int, private val worldheight: Int, override var preLoadJob: (LoadScreenBase) -> Unit) : LoadScreenBase() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
CommonResourcePool.addToLoadingList("basegame-gui-loadscrlayer01") {
|
CommonResourcePool.addToLoadingList("basegame.gui/loadscrlayer01") {
|
||||||
Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer01.png"))
|
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"))
|
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"))
|
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"))
|
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"))
|
Texture(ModMgr.getGdxFile("basegame", "gui/loadscr_layer05.png"))
|
||||||
}
|
}
|
||||||
CommonResourcePool.loadAll()
|
CommonResourcePool.loadAll()
|
||||||
@@ -51,14 +51,14 @@ open class FancyWorldReadLoadScreen(screenToBeLoaded: IngameInstance, private va
|
|||||||
val xoff = (Math.random() * (1024-764)/2).toInt()
|
val xoff = (Math.random() * (1024-764)/2).toInt()
|
||||||
|
|
||||||
val baseTileTex = arrayOf(
|
val baseTileTex = arrayOf(
|
||||||
CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer01"),
|
CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer01"),
|
||||||
CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer02"),
|
CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer02"),
|
||||||
CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer03"),
|
CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer03"),
|
||||||
CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer04"),
|
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-loadscrlayer05"),
|
CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer05"),
|
||||||
CommonResourcePool.getAsTexture("basegame-gui-loadscrlayer05"),
|
CommonResourcePool.getAsTexture("basegame.gui/loadscrlayer05"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val drawWidth = Toolkit.drawWidth
|
val drawWidth = Toolkit.drawWidth
|
||||||
|
|||||||
@@ -119,10 +119,9 @@ class FixtureJukebox : Electric, PlaysMusic {
|
|||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2f) {
|
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2f) {
|
||||||
startAudio(musicNowPlaying!!) {
|
startAudio(musicNowPlaying!!) {
|
||||||
it.filters[filterIndex] = Phono(
|
it.filters[filterIndex] = Phono(
|
||||||
ModMgr.getFile(
|
"basegame",
|
||||||
"basegame",
|
"audio/convolution/Soundwoofer - large_speaker_Marshall JVM 205C SM57 A 0 0 1.bin",
|
||||||
"audio/convolution/Soundwoofer - large_speaker_Marshall JVM 205C SM57 A 0 0 1.bin"
|
0f, 5f / 16f
|
||||||
), 0f, 5f / 16f
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user