mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
spectrogram on the scope
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user