spectrogram on the scope

This commit is contained in:
minjaesong
2023-12-21 20:51:00 +09:00
parent fefdf44e3e
commit 3a814955d0
2 changed files with 79 additions and 11 deletions

View File

@@ -2,19 +2,49 @@ package net.torvald.terrarum.audio.dsp
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
import com.jme3.math.FastMath
import net.torvald.terrarum.audio.*
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.AUDIO_BUFFER_SIZE
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED
import net.torvald.terrarum.ui.BasicDebugInfoWindow
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.STRIP_W
import net.torvald.terrarum.ui.Toolkit
import kotlin.math.roundToInt
import kotlin.math.*
class Scope : TerrarumAudioFilter() {
val backbufL = Array((4096f / TerrarumAudioMixerTrack.AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) { FloatArray(
TerrarumAudioMixerTrack.AUDIO_BUFFER_SIZE) }
val backbufR = Array((4096f / TerrarumAudioMixerTrack.AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) { FloatArray(
TerrarumAudioMixerTrack.AUDIO_BUFFER_SIZE) }
val backbufL = Array((4096f / AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) {
FloatArray(AUDIO_BUFFER_SIZE)
}
val backbufR = Array((4096f / AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) {
FloatArray(AUDIO_BUFFER_SIZE)
}
private val FFTSIZE = 1024
private val inBuf = Array(2) { FloatArray(FFTSIZE) }
private fun sin2(x: Double) = sin(x).pow(2)
private val chsum = ComplexArray(FloatArray(2*FFTSIZE))
private val fftOut = ComplexArray(FloatArray(2*FFTSIZE))
private val fftWin = FloatArray(FFTSIZE) { sin2(PI * it / FFTSIZE).toFloat() }
private val sqrt2p = 0.7071067811865475
private val oldFFTmagn = DoubleArray(FFTSIZE / 2) { 0.0 }
private fun push(samples: FloatArray, buf: FloatArray) {
if (samples.size >= FFTSIZE) {
// overwrite
System.arraycopy(samples, samples.size - buf.size, buf, 0, buf.size)
}
else {
// shift samples
System.arraycopy(buf, samples.size, buf, 0, buf.size - samples.size)
// write to the buf
System.arraycopy(samples, 0, buf, buf.size - samples.size, samples.size)
}
}
override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
// shift buffer
for (i in backbufL.lastIndex downTo 1) {
@@ -36,6 +66,16 @@ class Scope : TerrarumAudioFilter() {
backbufR[0][i] = y.toFloat()
}
// create (L+R)/2 array
push(inbuf[0], inBuf[0])
push(inbuf[1], inBuf[1])
for (i in 0 until FFTSIZE) {
chsum.reim[2*i] = ((inBuf[0][i] + inBuf[1][i]) / 2f) * fftWin[i]
}
// do fft
FFT.fftInto(chsum, fftOut)
// copy samples over
outbuf.forEachIndexed { index, outTrack ->
System.arraycopy(inbuf[index], 0, outTrack, 0, outTrack.size)
@@ -43,9 +83,35 @@ class Scope : TerrarumAudioFilter() {
}
private val halfStripW = STRIP_W / 2
private val scopePlotCol = Color(0x61b3df_33)
private val spectroPlotCol = Color(0xdf6fa0_aa.toInt())
private val lowlim = -96.0
override fun drawDebugView(batch: SpriteBatch, x: Int, y: Int) {
batch.color = spectroPlotCol
for (bin in 0 until FFTSIZE / 2) {
val freqL = (SAMPLING_RATED / FFTSIZE) * bin
val freqR = (SAMPLING_RATED / FFTSIZE) * (bin + 1)
val magn0 = fftOut.reim[2 * bin].absoluteValue / FFTSIZE * (freqR / 10.0) // apply slope
val magn = FastMath.interpolateLinear(BasicDebugInfoWindow.FFT_SMOOTHING_FACTOR, magn0, oldFFTmagn[bin])
val magnLog = fullscaleToDecibels(magn)
if (magnLog >= lowlim) {
val xL = linToLogPerc(freqL, 24.0, 24000.0).coerceIn(0.0, 1.0) * STRIP_W
val xR = linToLogPerc(freqR, 24.0, 24000.0).coerceIn(0.0, 1.0) * STRIP_W
val w = (xR - xL)
val h = (magnLog - lowlim) / lowlim * STRIP_W
Toolkit.fillArea(batch, x + xL.toFloat(), y + STRIP_W.toFloat(), w.toFloat(), h.toFloat())
}
oldFFTmagn[bin] = magn
}
batch.color = scopePlotCol
val xxs = backbufR
val yys = backbufL

View File

@@ -414,6 +414,13 @@ class BasicDebugInfoWindow : UICanvas() {
val FILTER_NAME_ACTIVE = Color(0xeeeeee_bf.toInt())
val FILTER_BYPASSED = Color(0xf1b934_bf.toInt())
fun getSmoothingFactor(sampleCount: Int) = (1.0 - (256.0 / sampleCount))
val PEAK_SMOOTHING_FACTOR = getSmoothingFactor(640)
val FFT_SMOOTHING_FACTOR = getSmoothingFactor(960)
val LAMP_SMOOTHING_FACTOR = getSmoothingFactor(3200)
val RMS_SMOOTHING_FACTOR = getSmoothingFactor(12000)
}
private val halfStripW = STRIP_W / 2
@@ -466,11 +473,6 @@ class BasicDebugInfoWindow : UICanvas() {
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 - (AUDIO_BUFFER_SIZE.toDouble() / sampleCount)
private val PEAK_SMOOTHING_FACTOR = getSmoothingFactor(640)
private val LAMP_SMOOTHING_FACTOR = getSmoothingFactor(3200)
private val RMS_SMOOTHING_FACTOR = getSmoothingFactor(12000)
val miniW = 28
val miniH = 35
private val LAMP_OVERRANGE = Color(0xff6666aa.toInt())