mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
more special purpose filters (audio samples will be added later)
This commit is contained in:
@@ -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>()
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
36
src/net/torvald/terrarum/audio/dsp/Phono.kt
Normal file
36
src/net/torvald/terrarum/audio/dsp/Phono.kt
Normal 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
|
||||
)
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user