From 28edba9bb390e32112a40694008ed658a76fc62c Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 11 Jan 2024 17:46:30 +0900 Subject: [PATCH] hopefully 'correct' binopan --- .../terrarum/audio/MixerTrackProcessor.kt | 13 +++- src/net/torvald/terrarum/audio/dsp/BinoPan.kt | 68 +++++++++++-------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt index c822e4602..a9f1838d9 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -5,6 +5,7 @@ import net.torvald.reflection.forceInvoke import net.torvald.terrarum.App import net.torvald.terrarum.audio.dsp.BinoPan import net.torvald.terrarum.audio.dsp.NullFilter +import net.torvald.terrarum.distBetweenActors import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.relativeXposition import net.torvald.terrarum.sqr @@ -111,11 +112,13 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra } else if (track.trackingTarget is ActorWithBody) { val relativeXpos = relativeXposition(AudioMixer.actorNowPlaying!!, track.trackingTarget as ActorWithBody) - track.volume = track.maxVolume * (1.0 - (relativeXpos.absoluteValue / distFalloff).pow(0.5)).coerceAtLeast(0.0) + val distFromActor = distBetweenActors(AudioMixer.actorNowPlaying!!, track.trackingTarget as ActorWithBody) + track.volume = track.maxVolume * getVolFun(distFromActor / distFalloff).coerceAtLeast(0.0) (track.filters[0] as BinoPan).pan = if (relativeXpos <= -distFalloff) -1f else if (relativeXpos >= distFalloff) 1f else ((2*asin(relativeXpos / distFalloff)) / Math.PI).toFloat() + // TODO lowpass filter by dist } } } @@ -234,6 +237,14 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra // } // uncomment to multithread } + // https://www.desmos.com/calculator/blcd4s69gl + private fun getVolFun(x: Double): Double { +// val K = 1.225 +// fun q(x: Double) = if (x >= 1.0) 0.5 else (K*x - K).pow(2.0) + 0.5 +// val x2 = x.pow(q(x)) + return decibelsToFullscale(-20.0 * x) + } + private fun FloatArray.applyVolume(volume: Float) = FloatArray(this.size) { (this[it] * volume) } /*private fun FloatArray.applyVolumeInline(volume: Float): FloatArray { for (i in this.indices) { diff --git a/src/net/torvald/terrarum/audio/dsp/BinoPan.kt b/src/net/torvald/terrarum/audio/dsp/BinoPan.kt index 42152d2c6..7b5e099b8 100644 --- a/src/net/torvald/terrarum/audio/dsp/BinoPan.kt +++ b/src/net/torvald/terrarum/audio/dsp/BinoPan.kt @@ -3,6 +3,7 @@ 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.App.printdbg import net.torvald.terrarum.audio.AudioMixer import net.torvald.terrarum.audio.TerrarumAudioMixerTrack import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.AUDIO_BUFFER_SIZE @@ -23,14 +24,10 @@ import kotlin.math.roundToInt */ class BinoPan(var pan: Float, var earDist: Float = 0.18f): TerrarumAudioFilter() { - private val PANNING_CONST = 3.0 // 3dB panning rule - - private val panUp = decibelsToFullscale(PANNING_CONST / 2.0).toFloat() - private val panDn = decibelsToFullscale(-PANNING_CONST / 2.0).toFloat() - private val delayLineL = FloatArray(AUDIO_BUFFER_SIZE) private val delayLineR = FloatArray(AUDIO_BUFFER_SIZE) + private fun getFrom(index: Float, buf0: FloatArray, buf1: FloatArray): Float { val index = index.toInt() // TODO resampling return if (index >= 0) buf1[index] @@ -44,47 +41,64 @@ class BinoPan(var pan: Float, var earDist: Float = 0.18f): TerrarumAudioFilter() private val outRs = Array(2) { FloatArray(AUDIO_BUFFER_SIZE) } - override fun thru(inbuf: List, outbuf: List) { - val angle = pan * 1.5707963f + companion object { + private val PANNING_CONST = 6.0 + private const val L = 0 + private const val R = 1 + + private val HALF_PI = (Math.PI / 2.0).toFloat() + } + + + private fun thru(sym: String, angleOffset: Float, inbuf: FloatArray, sumbuf: Array, delayLine: FloatArray) { + val pan = (pan + angleOffset / 90f).coerceIn(-1f, 1f) + + val angle = pan * HALF_PI val timeDiffMax = earDist / AudioMixer.SPEED_OF_SOUND * TerrarumAudioMixerTrack.SAMPLING_RATEF val delayInSamples = (timeDiffMax * FastMath.sin(angle)).absoluteValue val volMultDbThis = PANNING_CONST * pan.absoluteValue val volMultFsThis = decibelsToFullscale(volMultDbThis).toFloat() - val volMUltFsOther = 1f / volMultFsThis + val volMultFsOther = 1f / volMultFsThis if (pan >= 0) { - delays[0] = delayInSamples - delays[1] = 0f + delays[L] = delayInSamples + delays[R] = 0f } else { - delays[0] = 0f - delays[1] = delayInSamples + delays[L] = 0f + delays[R] = delayInSamples } if (pan >= 0) { - mults[0] = volMUltFsOther - mults[1] = volMultFsThis + mults[L] = volMultFsOther + mults[R] = volMultFsThis } else { - mults[0] = volMultFsThis - mults[1] = volMUltFsOther - } - - for (ch in 0..1) { - for (i in 0 until AUDIO_BUFFER_SIZE) { - outLs[ch][i] = getFrom(i - delays[ch], delayLineL, inbuf[0]) * mults[ch] - outRs[ch][i] = getFrom(i - delays[ch], delayLineR, inbuf[1]) * mults[ch] - } + mults[L] = volMultFsThis + mults[R] = volMultFsOther } for (i in 0 until AUDIO_BUFFER_SIZE) { - outbuf[0][i] = (outLs[0][i] * panUp + outLs[1][i] * panDn) / 2f - outbuf[1][i] = (outRs[0][i] * panDn + outRs[1][i] * panUp) / 2f + sumbuf[L][i] = mults[L] * getFrom(i - delays[L], delayLine, inbuf) + sumbuf[R][i] = mults[R] * getFrom(i - delays[R], delayLine, inbuf) } - push(inbuf[0], delayLineL) - push(inbuf[1], delayLineR) +// printdbg(this, "$sym\tpan=$pan, mults=${mults[L]}\t${mults[R]}") + } + override fun thru(inbuf: List, outbuf: List) { + thru("L", -90f, inbuf[L], outLs, delayLineL) + thru("R", +90f, inbuf[R], outRs, delayLineR) + + for (i in 0 until AUDIO_BUFFER_SIZE) { + val outL = (outLs[L][i] + outRs[L][i]) / 2f + val outR = (outLs[R][i] + outRs[R][i]) / 2f + outbuf[L][i] = outL + outbuf[R][i] = outR + } + + push(inbuf[L], delayLineL) + push(inbuf[R], delayLineR) } override fun drawDebugView(batch: SpriteBatch, x: Int, y: Int) {