i don't know if it's even working lol

This commit is contained in:
minjaesong
2024-12-25 15:18:07 +09:00
parent 9dc2dffef9
commit cc3e59796f
3 changed files with 199 additions and 0 deletions

View File

@@ -260,6 +260,7 @@ class AudioMixer : Disposable {
masterTrack.filters[0] = SoftClp
masterTrack.filters[1] = Buffer
// masterTrack.filters[1] = Comp(-24f, 5f, 0.5f)
masterTrack.filters[2] = Vecto(1.4142f)
masterTrack.filters[3] = Spectro()

View File

@@ -0,0 +1,134 @@
package net.torvald.terrarum.audio.dsp
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.jme3.math.FastMath
import net.torvald.terrarum.App
import net.torvald.terrarum.audio.decibelsToFullscale
import net.torvald.terrarum.audio.fullscaleToDecibels
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.COL_METER_GRAD2_YELLOW
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.COL_METER_GRAD_YELLOW
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.FILTER_NAME_ACTIVE
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.STRIP_W
import net.torvald.terrarum.ui.Toolkit
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
/**
* Created by minjaesong on 2024-12-24.
*/
class Comp(
val thresholdDB: Float,
val ratio: Float,
/** Knee is defined as the multiplier of the threshold. For example: if threshold is -12dB and kneeRatio is 0.5, the knee will be -24dB to -6dB */
val kneeRaio: Float
): TerrarumAudioFilter(), DspCompressor {
private val thresholdLinear = decibelsToFullscale(thresholdDB.toDouble())
private val kneeLinear = thresholdLinear * kneeRaio//decibelsToFullscale(kneeDB.toDouble())
private val makeupGain = (1.0 - 1.0 / ratio) * (-thresholdDB)
private val makeupGainLinear = decibelsToFullscale(makeupGain)
private var internalGainKnob = 1.0
private var internalGainKnobWithoutGain = 1.0
private var outputGainKnob = 1.0
private var outputGainKnobWithoutGain = 1.0 // used for debug view only
private val gainKnobWeight = 0.93 // outputGainKnob is updated per sample (48000 times per second)
private fun calcGainMinusM(sampleL: Float, sampleR: Float, thresholdLinear: Double, ratio: Float, kneeLinear: Double): Double {
// https://www.desmos.com/calculator/p3wufeoioi
val x = maxOf(sampleL.absoluteValue, sampleR.absoluteValue).toDouble()
val t = thresholdLinear
val k = kneeLinear
val r = ratio.toDouble()
// val M = (r-1) / r
val rx = r*x
val rt = r*t
val rtt = r*t*t
val kk = k*k
val kkr = k*k*r
val krt = k*r*t
val kr = k*r
val kt = k*t
val kx = k*x
val tt = t*t
val tx = t*x
val xx = x*x
val halfk = k/2
val fx = 1.0
val gx = (rx-r+t-x)/(rt-r)
val K = (kkr-kk+4*krt-8*kr+4*kt+4*rtt-4*tt)/(8*krt-8*kr)
val hx = ((r-1)*(kx-2*tx+xx))/(2*krt-2*kr)+K
return if (x < t-halfk)
fx
else if (1 > x && x > t+halfk)
gx
else if (t-halfk <= x && x <= t+halfk)
hx
else
(1/r)
}
override val downForce = arrayOf(1.0f, 1.0f)
private var maxReduction = 1.0
override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
downForce.fill(1.0f)
maxReduction = 10000.0
val outL = outbuf[0]
val innL = inbuf[0]
val outR = outbuf[1]
val innR = inbuf[1]
for (i in innL.indices) {
// do the compression
val sampleL = innL[i]
val sampleR = innR[i]
internalGainKnobWithoutGain = calcGainMinusM(sampleL, sampleR, thresholdLinear, ratio, kneeLinear)
internalGainKnob = internalGainKnobWithoutGain * makeupGainLinear
outputGainKnobWithoutGain = FastMath.interpolateLinear(gainKnobWeight, internalGainKnobWithoutGain, outputGainKnobWithoutGain)
outputGainKnob = FastMath.interpolateLinear(gainKnobWeight, internalGainKnob, outputGainKnob)
outL[i] = (sampleL * internalGainKnob).toFloat()
outR[i] = (sampleR * internalGainKnob).toFloat()
// calculate the downforce
maxReduction = minOf(maxReduction, outputGainKnobWithoutGain)
downForce[0] = maxReduction.toFloat()
downForce[1] = maxReduction.toFloat()
}
}
override fun drawDebugView(batch: SpriteBatch, x: Int, y: Int) {
val reductionDB = fullscaleToDecibels(maxReduction)
val perc = (reductionDB / (-0.5*makeupGain)).toFloat().coerceIn(0f, 1f)
batch.color = COL_METER_GRAD2_YELLOW
Toolkit.fillArea(batch, x.toFloat() + STRIP_W, y.toFloat(), -STRIP_W * perc, 14f)
batch.color = COL_METER_GRAD_YELLOW
Toolkit.fillArea(batch, x.toFloat() + STRIP_W, y+14f, -STRIP_W * perc, 2f)
batch.color = FILTER_NAME_ACTIVE
App.fontSmallNumbers.draw(batch, "C:${reductionDB.absoluteValue.times(100).roundToInt().div(100f)}", x+3f, y+1f)
}
override val debugViewHeight: Int = 16
override fun copyParamsFrom(other: TerrarumAudioFilter) {
TODO()
}
private fun Double.unNaN(d: Double): Double {
return if (this.isNaN()) d else this
}
}

View File

@@ -0,0 +1,64 @@
package net.torvald.terrarum.audio.dsp
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
/**
* Created by minjaesong on 2024-12-22.
*/
class MultibandComp: TerrarumAudioFilter() {
private val ratio = 2f
private val bands = listOf(
160f, 1100f, 7500f
)
private val bandFilters = bands.mapIndexed { index: Int, band: Float ->
if (index == 0)
Lowpass(band)
else
Bandpass(bands[index-1], band)
} + Highpass(bands.last())
private var midbufLen = App.audioBufferSize
private var midbufs = bandFilters.map {
listOf(FloatArray(midbufLen), FloatArray(midbufLen))
}
private fun resizeMidbufs(newLen: Int) {
midbufLen = newLen
midbufs = bandFilters.map {
listOf(FloatArray(midbufLen), FloatArray(midbufLen))
}
}
private val statelessComp = Comp(-24f, ratio, 6f)
override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
(bandFilters zip midbufs).forEach { (filter, buf) ->
// pass thru the band filters
filter.thru(inbuf, buf)
// apply the comp fun
for (ch in outbuf.indices) {
for (sample in 0 until midbufLen) {
statelessComp.thru(buf, buf)
// and add up the results to the outbuf
outbuf[ch][sample] += buf[ch][sample]
}
}
}
}
override fun drawDebugView(batch: SpriteBatch, x: Int, y: Int) {
}
override val debugViewHeight: Int = 16
override fun copyParamsFrom(other: TerrarumAudioFilter) {
TODO()
}
}