mixer overlay

This commit is contained in:
minjaesong
2023-11-18 20:27:32 +09:00
parent 5c5f526d16
commit 60d793f753
6 changed files with 110 additions and 30 deletions

View File

@@ -5,6 +5,8 @@ import com.badlogic.gdx.backends.lwjgl3.audio.Lwjgl3Audio
import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath
import net.torvald.terrarum.App
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.INDEX_AMB
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.INDEX_BGM
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATEF
import net.torvald.terrarum.modulebasegame.MusicContainer
@@ -27,22 +29,23 @@ object AudioMixer: Disposable {
/** Returns a (master volume * bgm volume) */
val musicVolume: Double
get() = (App.getConfigDouble("bgmvolume") * App.getConfigDouble("mastervolume"))
get() = App.getConfigDouble("bgmvolume")
/** Returns a (master volume * sfx volume */
val ambientVolume: Double
get() = (App.getConfigDouble("sfxvolume") * App.getConfigDouble("mastervolume"))
get() = App.getConfigDouble("sfxvolume")
val tracks = Array(10) { TerrarumAudioMixerTrack(
if (it == 0) "BGM Track"
else if (it == 1) "AMB Track"
else "Audio Track #${it+1}"
if (it == 0) "BGM"
else if (it == 1) "AMB"
else "Trk${it+1}"
) }
val masterTrack = TerrarumAudioMixerTrack("Master", true).also { master ->
tracks.forEach { master.addSidechainInput(it, 1.0) }
master.volume = masterVolume
master.filters[0] = Buffer
tracks.forEachIndexed { i, it -> master.addSidechainInput(it, if (i == INDEX_BGM) musicVolume else if (i == INDEX_AMB) ambientVolume else 1.0) }
}
val musicTrack: TerrarumAudioMixerTrack
@@ -59,6 +62,8 @@ object AudioMixer: Disposable {
private var fadeLength = DEFAULT_FADEOUT_LEN
private var fadeoutFired = false
private var fadeinFired = false
private var fadeTarget = 0.0
private var fadeStart = 0.0
private var lpAkku = 0.0
private var lpLength = 0.4
@@ -75,24 +80,28 @@ object AudioMixer: Disposable {
if (fadeoutFired) {
fadeAkku += delta
musicTrack.volume = (musicVolume * (1.0 - (fadeAkku / fadeLength))).coerceIn(0.0, 1.0)
val step = fadeAkku / fadeLength
musicTrack.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget)
if (fadeAkku >= fadeLength) {
fadeoutFired = false
musicTrack.volume = 0.0
musicTrack.currentTrack = null
musicTrack.volume = fadeTarget
if (fadeTarget == 0.0)
musicTrack.currentTrack = null
}
}
else if (fadeinFired) {
fadeAkku += delta
musicTrack.volume = (musicVolume * (fadeAkku / fadeLength)).coerceIn(0.0, 1.0)
val step = fadeAkku / fadeLength
musicTrack.volume = FastMath.interpolateLinear(step, fadeStart, fadeTarget)
if (musicTrack.isPlaying == false) {
musicTrack.play()
}
if (fadeAkku >= fadeLength) {
musicTrack.volume = musicVolume
musicTrack.volume = fadeTarget
fadeinFired = false
}
}
@@ -100,7 +109,7 @@ object AudioMixer: Disposable {
if (lpOutFired) {
lpAkku += delta
val x = (lpAkku / lpLength).coerceIn(0.0, 1.0)
val x = lpAkku / lpLength
val q = 400.0
val step = (q.pow(x) - 1) / (q - 1) // https://www.desmos.com/calculator/sttaq2qhzm
val cutoff = FastMath.interpolateLinear(step, SAMPLING_RATED / 100.0, SAMPLING_RATED)
@@ -113,7 +122,7 @@ object AudioMixer: Disposable {
}
else if (lpInFired) {
lpAkku += delta
val x = (lpAkku / lpLength).coerceIn(0.0, 1.0)
val x = lpAkku / lpLength
val q = 400.0
val step = log((q-1) * x + 1.0, q) // https://www.desmos.com/calculator/sttaq2qhzm
val cutoff = FastMath.interpolateLinear(step, SAMPLING_RATED, SAMPLING_RATED / 100.0)
@@ -129,7 +138,7 @@ object AudioMixer: Disposable {
if (musicTrack.isPlaying != true && musicTrack.nextTrack != null) {
// printdbg(this, "Playing next music: ${nextMusic!!.name}")
musicTrack.queueNext(null)
musicTrack.volume = musicVolume
musicTrack.volume = 1.0
musicTrack.play()
}
}
@@ -145,19 +154,23 @@ object AudioMixer: Disposable {
requestFadeOut(DEFAULT_FADEOUT_LEN)
}
fun requestFadeOut(length: Double) {
fun requestFadeOut(length: Double, target: Double = 0.0) {
if (!fadeoutFired) {
fadeLength = length.coerceAtLeast(1.0/1024.0)
fadeAkku = 0.0
fadeoutFired = true
fadeTarget = target
fadeStart = musicTrack.volume
}
}
fun requestFadeIn(length: Double) {
fun requestFadeIn(length: Double, target: Double = 1.0) {
if (!fadeinFired) {
fadeLength = length.coerceAtLeast(1.0/1024.0)
fadeAkku = 0.0
fadeinFired = true
fadeTarget = target
fadeStart = musicTrack.volume
}
}

View File

@@ -2,6 +2,8 @@ package net.torvald.terrarum.audio
import com.badlogic.gdx.utils.Queue
import net.torvald.reflection.forceInvoke
import net.torvald.terrarum.audio.AudioMixer.masterVolume
import net.torvald.terrarum.audio.AudioMixer.musicVolume
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE
import kotlin.math.absoluteValue
@@ -35,7 +37,8 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
if (true) println("[AudioAdapter ${track.name}] $msg")
}
override fun run() {
while (running) { synchronized(pauseLock) {
while (running) {
synchronized(pauseLock) {
if (!running) { // may have changed while waiting to
// synchronize on pauseLock
breakBomb = true
@@ -90,11 +93,11 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
if (track.isMaster) {
// TEST CODE must combine all the inputs
track.sidechainInputs[0]?.let {
samplesL0 = it.first.processor.fout0[0]
samplesR0 = it.first.processor.fout0[1]
samplesL1 = it.first.processor.fout1[0]
samplesR1 = it.first.processor.fout1[1]
track.sidechainInputs[TerrarumAudioMixerTrack.INDEX_BGM]?.let {
samplesL0 = it.first.processor.fout0[0].applyVolume(musicVolume)
samplesR0 = it.first.processor.fout0[1].applyVolume(musicVolume)
samplesL1 = it.first.processor.fout1[0].applyVolume(musicVolume)
samplesR1 = it.first.processor.fout1[1].applyVolume(musicVolume)
}
@@ -164,7 +167,6 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
// by this time, the output buffer is filled with processed results, pause the execution
if (!track.isMaster) {
this.pause()
}
else {
@@ -176,7 +178,9 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
}
// printdbg("PUSHE; Queue size: ${track.pcmQueue.size}")
track.pcmQueue.addLast(fout1)
val masvol = masterVolume
track.volume = masvol
track.pcmQueue.addLast(fout1.map { it.applyVolume(masvol) })
}
// spin
@@ -210,8 +214,11 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
pauseLock.notifyAll() // Unblocks thread
}
}
private fun FloatArray.applyVolume(musicVolume: Double) = FloatArray(this.size) { (this[it] * musicVolume).toFloat() }
}
private fun <T> Queue<T>.removeFirstOrElse(function: () -> T): T {
return if (this.isEmpty) {
this.removeFirst()

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.utils.Queue
import net.torvald.reflection.forceInvoke
import net.torvald.terrarum.getHashStr
import net.torvald.terrarum.modulebasegame.MusicContainer
import java.lang.Thread.MAX_PRIORITY
import kotlin.math.log10
import kotlin.math.pow
@@ -18,7 +19,10 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false):
const val SAMPLING_RATE = 48000
const val SAMPLING_RATEF = 48000f
const val SAMPLING_RATED = 48000.0
const val BUFFER_SIZE = 8000
const val BUFFER_SIZE = 12000
const val INDEX_BGM = 0
const val INDEX_AMB = 1
}
val hash = getHashStr()
@@ -128,6 +132,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false):
internal var processor = MixerTrackProcessor(BUFFER_SIZE, SAMPLING_RATE, this)
private val processorThread = Thread(processor).also {
it.priority = MAX_PRIORITY // higher = more predictable; audio delay is very noticeable so it gets high priority
it.start()
}
internal var pcmQueue = Queue<List<FloatArray>>()
@@ -141,6 +146,7 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false):
if (isMaster) {
queueDispatcher = FeedSamplesToAdev(BUFFER_SIZE, SAMPLING_RATE, this)
queueDispatcherThread = Thread(queueDispatcher).also {
it.priority = MAX_PRIORITY // higher = more predictable; audio delay is very noticeable so it gets high priority
it.start()
}
}