mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +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.
|
||||
*/
|
||||
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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user