more special purpose filters (audio samples will be added later)

This commit is contained in:
minjaesong
2024-01-24 15:05:19 +09:00
parent e5b1eeb9d2
commit 76f7b2a145
5 changed files with 114 additions and 12 deletions

View File

@@ -54,7 +54,7 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
}
}
private val MP3_CHUNK_SIZE = 1152 // 1152 for 32k-48k, 576 for 16k-24k, 384 for 8k-12k
const val MP3_CHUNK_SIZE = 1152 // 1152 for 32k-48k, 576 for 16k-24k, 384 for 8k-12k
private val bufLut = HashMap<Pair<Int, Int>, Int>()

View File

@@ -1,7 +1,11 @@
package net.torvald.terrarum.audio.dsp
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.AudioProcessBuf.Companion.MP3_CHUNK_SIZE
import net.torvald.terrarum.serialise.toUint
import java.io.File
import kotlin.math.absoluteValue
import kotlin.math.tanh
@@ -15,25 +19,73 @@ import kotlin.math.tanh
*
* Created by minjaesong on 2024-01-21.
*/
class LoFi(ir: File, val crossfeed: Float, gain: Float = 1f / 256f): TerrarumAudioFilter(), DspCompressor {
open class LoFi(static: File, ir: File, val crossfeed: Float, gain: Float = 1f / 256f): TerrarumAudioFilter(), DspCompressor {
override val downForce = arrayOf(1.0f, 1.0f)
internal val staticSample: List<FloatArray>
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)
private val imm = 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))
override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
convolver.thru(inbuf, imm)
staticMixThru(inbuf, immAfterStaticMix)
convolver.thru(immAfterStaticMix, immAfterConvolv)
saturatorThru(immAfterConvolv, outbuf)
}
for (ch in imm.indices) {
val inn = imm[ch]
val out = outbuf[ch]
private fun staticMixThru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
for (h in 0 until App.audioBufferSize) {
outbuf[0][h] = inbuf[0][h] + staticSample[0][staticSamplePlayCursor]
outbuf[1][h] = inbuf[1][h] + staticSample[1][staticSamplePlayCursor]
staticSamplePlayCursor = (staticSamplePlayCursor + 1) % staticSample[0].size
}
}
for (i in inn.indices) {
val u = inn[i]
val v = tanh(u)
private fun saturatorThru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
for (ch in inbuf.indices) {
for (i in inbuf[ch].indices) {
val u = inbuf[ch][i]
val v = saturate(u)
val diff = (v.absoluteValue / u.absoluteValue)
out[i] = v
outbuf[ch][i] = v
if (!diff.isNaN()) {
downForce[ch] = minOf(downForce[ch], diff)
@@ -42,6 +94,14 @@ class LoFi(ir: File, val crossfeed: Float, gain: Float = 1f / 256f): TerrarumAud
}
}
/**
* Saturation function aka Voltage Transfer Characteristic Curve.
* Default function is `tanh(x)`
*/
open fun saturate(v: Float): Float {
return tanh(v)
}
override fun drawDebugView(batch: SpriteBatch, x: Int, y: Int) {
}

View File

@@ -0,0 +1,36 @@
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
)
/**
* Hiss of the magnetic tape.
*
* 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
)
/**
* Static noise of the fictional Holotape, based on RCA Holotape and not Fallout Holotape.
*
* You can argue "high-tech storage medium like Holotape should hold digital audio" but where's the fun in that?
*
* 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
)

View File

@@ -6,6 +6,11 @@ import kotlin.math.pow
import kotlin.math.sqrt
/**
* This filter compresses the input signal using a fixed artificial Voltage Transfer Characteristic curve,
* where the curve is linear on the intensity lower than -3dB, then up to +3.5dB the curve flattens
* (compression ratio steadily reaches infinity) gradually, using simple quadratic curve, then above
* +3.5dB the curve is completely flat and the compression ratio is infinity.
*
* Created by minjaesong on 2023-11-20.
*/
object SoftClp : TerrarumAudioFilter(), DspCompressor {

View File

@@ -13,6 +13,7 @@ import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN
import net.torvald.terrarum.audio.dsp.Convolv
import net.torvald.terrarum.audio.dsp.LoFi
import net.torvald.terrarum.audio.dsp.NullFilter
import net.torvald.terrarum.audio.dsp.Phono
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.langpack.Lang
@@ -117,7 +118,7 @@ class FixtureJukebox : Electric, PlaysMusic {
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2f) {
startAudio(musicNowPlaying!!) {
it.filters[filterIndex] = LoFi(
it.filters[filterIndex] = Phono(
ModMgr.getFile(
"basegame",
"audio/convolution/Soundwoofer - large_speaker_Marshall JVM 205C SM57 A 0 0 1.bin"