mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
meter rms and smoothing
This commit is contained in:
@@ -2,7 +2,9 @@ package net.torvald.terrarum.audio
|
||||
|
||||
import com.badlogic.gdx.utils.Queue
|
||||
import net.torvald.reflection.forceInvoke
|
||||
import net.torvald.terrarum.sqr
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.math.tanh
|
||||
|
||||
/**
|
||||
@@ -28,8 +30,9 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
|
||||
private var fout0 = listOf(emptyBuf, emptyBuf)
|
||||
private var fout1 = listOf(emptyBuf, emptyBuf)
|
||||
|
||||
var maxSigLevel = arrayOf(0.0, 0.0); private set
|
||||
var hasClipping = arrayOf(false, false); private set
|
||||
val maxSigLevel = arrayOf(0.0, 0.0)
|
||||
val maxRMS = arrayOf(0.0, 0.0)
|
||||
val hasClipping = arrayOf(false, false)
|
||||
|
||||
private var breakBomb = false
|
||||
|
||||
@@ -149,6 +152,9 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
|
||||
fout1.map { it.maxOf { it.absoluteValue } }.forEachIndexed { index, fl ->
|
||||
maxSigLevel[index] = fl.toDouble()
|
||||
}
|
||||
fout1.map { it.sumOf { it.sqr().toDouble() } }.forEachIndexed { index, fl ->
|
||||
maxRMS[index] = sqrt(fl / (bufferSize / 4))
|
||||
}
|
||||
hasClipping.fill(false)
|
||||
fout1.forEachIndexed { index, floats ->
|
||||
var lastSample = floats[0]
|
||||
@@ -164,6 +170,7 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
|
||||
}
|
||||
else {
|
||||
maxSigLevel.fill(0.0)
|
||||
maxRMS.fill(0.0)
|
||||
hasClipping.fill(false)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ object NullFilter : TerrarumAudioFilter() {
|
||||
}
|
||||
|
||||
object SoftLim : TerrarumAudioFilter() {
|
||||
var downForce = 1.0f; private set
|
||||
val downForce = arrayOf(1.0f, 1.0f)
|
||||
|
||||
override fun thru(inbuf0: List<FloatArray>, inbuf1: List<FloatArray>, outbuf0: List<FloatArray>, outbuf1: List<FloatArray>) {
|
||||
downForce = 1.0f
|
||||
downForce.fill(1.0f)
|
||||
|
||||
for (ch in inbuf1.indices) {
|
||||
val inn = inbuf1[ch]
|
||||
@@ -44,7 +44,7 @@ object SoftLim : TerrarumAudioFilter() {
|
||||
out[i] = v
|
||||
|
||||
if (!diff.isNaN()) {
|
||||
downForce = minOf(downForce, diff)
|
||||
downForce[ch] = minOf(downForce[ch], diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.Terrarum.mouseTileX
|
||||
import net.torvald.terrarum.Terrarum.mouseTileY
|
||||
@@ -404,7 +405,8 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
private val meterTroughHeight = 16 * 11 + 5
|
||||
private val meterHeight = meterTroughHeight - 4
|
||||
|
||||
private val mixerLastTimeHadClipping = (AudioMixer.tracks + AudioMixer.masterTrack).map { arrayOf(0L, 0L) }
|
||||
private val trackCount = (AudioMixer.tracks + AudioMixer.masterTrack).size
|
||||
private val mixerLastTimeHadClipping = Array(trackCount) { arrayOf(0L, 0L) }
|
||||
private val clippingHoldTime = 60000L * 3 // 3 mins
|
||||
|
||||
private fun drawAudioMixer(batch: SpriteBatch) {
|
||||
@@ -430,6 +432,14 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
|
||||
private val dbLow = 48.0
|
||||
|
||||
private val oldPeak = Array(trackCount) { arrayOf(0.0, 0.0) }
|
||||
private val oldRMS = Array(trackCount) { arrayOf(0.0, 0.0) }
|
||||
private val oldComp = Array(trackCount) { arrayOf(0.0, 0.0) }
|
||||
|
||||
private fun getSmoothingFactor(sampleCount: Int) = 1.0 - (BUFFER_SIZE / (4.0 * sampleCount))
|
||||
private val PEAK_SMOOTHING_FACTOR = getSmoothingFactor(640)
|
||||
private val RMS_SMOOTHING_FACTOR = getSmoothingFactor(12000)
|
||||
|
||||
private fun drawStrip(batch: SpriteBatch, x: Int, y: Int, track: TerrarumAudioMixerTrack, index: Int) {
|
||||
// back
|
||||
batch.color = if (track.isMaster) COL_WELL4 else if (track.isBus) COL_WELL3 else trackBack[index % 2]
|
||||
@@ -487,10 +497,10 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
|
||||
// slider text
|
||||
val dB = track.dBfs
|
||||
val dBstr = dB.toIntAndFrac(2,1)
|
||||
val dBstr = dB.toIntAndFrac(3,1)
|
||||
val dBfs = dB.coerceIn(-dbLow, 0.0).plus(dbLow).div(dbLow).toFloat()
|
||||
batch.color = FILTER_NAME_ACTIVE
|
||||
App.fontSmallNumbers.draw(batch, dBstr, sliderX - 16f, faderY+1f)
|
||||
App.fontSmallNumbers.draw(batch, dBstr, sliderX - 23f, faderY+1f)
|
||||
|
||||
// fader trough
|
||||
batch.color = COL_METER_TROUGH
|
||||
@@ -514,21 +524,43 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
// fader
|
||||
batch.color = COL_METER_BAR
|
||||
for (ch in 0..1) {
|
||||
val fs = track.processor.maxSigLevel[ch]
|
||||
val fs = FastMath.interpolateLinear(PEAK_SMOOTHING_FACTOR, track.processor.maxSigLevel[ch], oldPeak[index][ch])
|
||||
val dBfs = fullscaleToDecibels(fs)
|
||||
|
||||
val x = x + 19f + 7 * ch
|
||||
val h = ((dBfs + dbLow) / dbLow * -meterHeight).coerceAtMost(0.0).toFloat()
|
||||
Toolkit.fillArea(batch, x, faderY + 18f + meterHeight, 6f, h)
|
||||
|
||||
oldPeak[index][ch] = fs
|
||||
}
|
||||
|
||||
// rms marker
|
||||
batch.color = FILTER_NAME_ACTIVE
|
||||
for (ch in 0..1) {
|
||||
val rms = FastMath.interpolateLinear(RMS_SMOOTHING_FACTOR, track.processor.maxRMS[ch], oldRMS[index][ch])
|
||||
val dBfs = fullscaleToDecibels(rms)
|
||||
|
||||
val x = x + 19f + 7 * ch
|
||||
val h = ((dBfs + dbLow) / dbLow * -meterHeight).coerceAtMost(0.0).toFloat()
|
||||
Toolkit.fillArea(batch, x, faderY + 19f + meterHeight + h, 6f, -2f)
|
||||
|
||||
oldRMS[index][ch] = rms
|
||||
}
|
||||
|
||||
// comp marker
|
||||
track.filters.filterIsInstance<SoftLim>().firstOrNull()?.let {
|
||||
val downDb = fullscaleToDecibels(it.downForce.toDouble())
|
||||
if (downDb.isFinite()) {
|
||||
val h = meterHeight + ((downDb + dbLow) / dbLow * -meterHeight).coerceAtMost(0.0).toFloat()
|
||||
batch.color = COL_METER_COMP_BAR
|
||||
Toolkit.fillArea(batch, x + 33f, faderY + 18f, 2f, h)
|
||||
for (ch in 0..1) {
|
||||
val downForceNow = it.downForce[ch] * 1.0
|
||||
if (downForceNow != 0.0) {
|
||||
val down = FastMath.interpolateLinear(PEAK_SMOOTHING_FACTOR, downForceNow, oldComp[index][ch])
|
||||
val dBfs = fullscaleToDecibels(down)
|
||||
|
||||
val h = meterHeight + ((dBfs + dbLow) / dbLow * -meterHeight).coerceAtMost(0.0).toFloat()
|
||||
batch.color = COL_METER_COMP_BAR
|
||||
Toolkit.fillArea(batch, x + 16f + ch * 17, faderY + 18f, 2f, h)
|
||||
|
||||
oldComp[index][ch] = down
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user