mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-15 04:54:05 +09:00
softlimiter filter for master
This commit is contained in:
@@ -5,13 +5,8 @@ import com.badlogic.gdx.backends.lwjgl3.audio.Lwjgl3Audio
|
|||||||
import com.badlogic.gdx.utils.Disposable
|
import com.badlogic.gdx.utils.Disposable
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
import net.torvald.terrarum.App
|
import net.torvald.terrarum.App
|
||||||
import net.torvald.terrarum.audio.MixerTrackProcessor.Companion.BACK_BUF_COUNT
|
|
||||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.BUFFER_SIZE
|
|
||||||
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_RATE
|
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE
|
||||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED
|
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED
|
||||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATEF
|
|
||||||
import net.torvald.terrarum.modulebasegame.MusicContainer
|
import net.torvald.terrarum.modulebasegame.MusicContainer
|
||||||
import net.torvald.terrarum.tryDispose
|
import net.torvald.terrarum.tryDispose
|
||||||
import java.lang.Thread.MAX_PRIORITY
|
import java.lang.Thread.MAX_PRIORITY
|
||||||
@@ -100,7 +95,8 @@ object AudioMixer: Disposable {
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
masterTrack.filters[0] = Buffer
|
masterTrack.filters[0] = SoftLim
|
||||||
|
masterTrack.filters[1] = Buffer
|
||||||
|
|
||||||
fadeBus.addSidechainInput(musicTrack, 1.0)
|
fadeBus.addSidechainInput(musicTrack, 1.0)
|
||||||
fadeBus.addSidechainInput(ambientTrack, 1.0)
|
fadeBus.addSidechainInput(ambientTrack, 1.0)
|
||||||
@@ -165,10 +161,13 @@ object AudioMixer: Disposable {
|
|||||||
if (req.fadeAkku >= req.fadeLength) {
|
if (req.fadeAkku >= req.fadeLength) {
|
||||||
req.fadeoutFired = false
|
req.fadeoutFired = false
|
||||||
track.volume = req.fadeTarget
|
track.volume = req.fadeTarget
|
||||||
track.volume = req.fadeTarget
|
|
||||||
|
|
||||||
if (req.fadeTarget == 0.0) {
|
// stop streaming if fadeBus is muted
|
||||||
track.currentTrack = null
|
if (req.fadeTarget == 0.0 && track == fadeBus) {
|
||||||
|
musicTrack.currentTrack = null
|
||||||
|
musicTrack.streamPlaying = false
|
||||||
|
ambientTrack.currentTrack = null
|
||||||
|
ambientTrack.streamPlaying = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,12 +215,17 @@ object AudioMixer: Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (musicTrack.isPlaying != true && musicTrack.nextTrack != null) {
|
if (!musicTrack.isPlaying && musicTrack.nextTrack != null) {
|
||||||
// printdbg(this, "Playing next music: ${nextMusic!!.name}")
|
|
||||||
musicTrack.queueNext(null)
|
musicTrack.queueNext(null)
|
||||||
fadeBus.volume = 1.0
|
fadeBus.volume = 1.0
|
||||||
musicTrack.play()
|
musicTrack.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ambientTrack.isPlaying && ambientTrack.nextTrack != null) {
|
||||||
|
ambientTrack.queueNext(null)
|
||||||
|
requestFadeIn(ambientTrack, DEFAULT_FADEOUT_LEN * 4, 1.0, 0.00001)
|
||||||
|
ambientTrack.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startMusic(song: MusicContainer) {
|
fun startMusic(song: MusicContainer) {
|
||||||
@@ -240,31 +244,32 @@ object AudioMixer: Disposable {
|
|||||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||||
}
|
}
|
||||||
ambientTrack.nextTrack = song
|
ambientTrack.nextTrack = song
|
||||||
|
// fade will be processed by the update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopAmb() {
|
fun stopAmb() {
|
||||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
requestFadeOut(ambientTrack, DEFAULT_FADEOUT_LEN * 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestFadeOut(track: TerrarumAudioMixerTrack, length: Double, target: Double = 0.0) {
|
fun requestFadeOut(track: TerrarumAudioMixerTrack, length: Double, target: Double = 0.0, source: Double? = null) {
|
||||||
val req = fadeReqs[track]!!
|
val req = fadeReqs[track]!!
|
||||||
if (!req.fadeoutFired) {
|
if (!req.fadeoutFired) {
|
||||||
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||||
req.fadeAkku = 0.0
|
req.fadeAkku = 0.0
|
||||||
req.fadeoutFired = true
|
req.fadeoutFired = true
|
||||||
req.fadeTarget = target * track.maxVolume
|
req.fadeTarget = target * track.maxVolume
|
||||||
req.fadeStart = fadeBus.volume
|
req.fadeStart = source ?: fadeBus.volume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestFadeIn(track: TerrarumAudioMixerTrack, length: Double, target: Double = 1.0) {
|
fun requestFadeIn(track: TerrarumAudioMixerTrack, length: Double, target: Double = 1.0, source: Double? = null) {
|
||||||
val req = fadeReqs[track]!!
|
val req = fadeReqs[track]!!
|
||||||
if (!req.fadeinFired) {
|
if (!req.fadeinFired) {
|
||||||
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
req.fadeLength = length.coerceAtLeast(1.0/1024.0)
|
||||||
req.fadeAkku = 0.0
|
req.fadeAkku = 0.0
|
||||||
req.fadeinFired = true
|
req.fadeinFired = true
|
||||||
req.fadeTarget = target * track.maxVolume
|
req.fadeTarget = target * track.maxVolume
|
||||||
req.fadeStart = fadeBus.volume
|
req.fadeStart = source ?: fadeBus.volume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package net.torvald.terrarum.audio
|
|||||||
|
|
||||||
import com.badlogic.gdx.utils.Queue
|
import com.badlogic.gdx.utils.Queue
|
||||||
import net.torvald.reflection.forceInvoke
|
import net.torvald.reflection.forceInvoke
|
||||||
import org.apache.commons.math3.special.Erf.erf
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.tanh
|
import kotlin.math.tanh
|
||||||
|
|
||||||
@@ -105,20 +104,6 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
|
|||||||
samplesR1!![i] += side.processor.fout1[1][i] * (mix * track.volume).toFloat()
|
samplesR1!![i] += side.processor.fout1[1][i] * (mix * track.volume).toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// de-clip using sigmoid function
|
|
||||||
for (i in samplesL0.indices) {
|
|
||||||
samplesL0[i] = erf(samplesL0[i].toDouble()).toFloat()
|
|
||||||
samplesR0[i] = erf(samplesR0[i].toDouble()).toFloat()
|
|
||||||
samplesL1[i] = erf(samplesL1[i].toDouble()).toFloat()
|
|
||||||
samplesR1[i] = erf(samplesR1[i].toDouble()).toFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*track.sidechainInputs[TerrarumAudioMixerTrack.INDEX_BGM]?.let { (side, mix) ->
|
|
||||||
samplesL0 = side.processor.fout0[0].applyVolume((mix * track.volume).toFloat()) // must not applyVolumeInline
|
|
||||||
samplesR0 = side.processor.fout0[1].applyVolume((mix * track.volume).toFloat())
|
|
||||||
samplesL1 = side.processor.fout1[0].applyVolume((mix * track.volume).toFloat())
|
|
||||||
samplesR1 = side.processor.fout1[1].applyVolume((mix * track.volume).toFloat())
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
// source channel: skip processing if there's no active input
|
// source channel: skip processing if there's no active input
|
||||||
// else if (track.getSidechains().any { it != null && !it.isBus && !it.isMaster && !it.streamPlaying } && !track.streamPlaying) {
|
// else if (track.getSidechains().any { it != null && !it.isBus && !it.isMaster && !it.streamPlaying } && !track.streamPlaying) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.torvald.terrarum.audio
|
package net.torvald.terrarum.audio
|
||||||
|
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
|
import kotlin.math.tanh
|
||||||
|
|
||||||
abstract class TerrarumAudioFilter {
|
abstract class TerrarumAudioFilter {
|
||||||
var bypass = false
|
var bypass = false
|
||||||
@@ -23,6 +24,19 @@ object NullFilter : TerrarumAudioFilter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object SoftLim : TerrarumAudioFilter() {
|
||||||
|
override fun thru(inbuf0: List<FloatArray>, inbuf1: List<FloatArray>, outbuf0: List<FloatArray>, outbuf1: List<FloatArray>) {
|
||||||
|
for (ch in inbuf1.indices) {
|
||||||
|
val inn = inbuf1[ch]
|
||||||
|
val out = outbuf1[ch]
|
||||||
|
|
||||||
|
for (i in inn.indices) {
|
||||||
|
out[i] = tanh(inn[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Lowpass(cutoff0: Float, val rate: Int): TerrarumAudioFilter() {
|
class Lowpass(cutoff0: Float, val rate: Int): TerrarumAudioFilter() {
|
||||||
|
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ class TerrarumAudioMixerTrack(val name: String, val isMaster: Boolean = false, v
|
|||||||
// currentTrack?.gdxMusic?.play()
|
// currentTrack?.gdxMusic?.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
val isPlaying: Boolean?
|
val isPlaying: Boolean
|
||||||
get() = currentTrack?.gdxMusic?.isPlaying
|
get() = streamPlaying//currentTrack?.gdxMusic?.isPlaying
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
/*if (isMaster) { // uncomment to multithread
|
/*if (isMaster) { // uncomment to multithread
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
it.nameWithoutExtension.replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
it.nameWithoutExtension.replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
||||||
it,
|
it,
|
||||||
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath))
|
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath))
|
||||||
) { stopMusic() }
|
) { stopAmbient() }
|
||||||
}
|
}
|
||||||
catch (e: GdxRuntimeException) {
|
catch (e: GdxRuntimeException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -123,6 +123,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
|
|
||||||
// val ingame = ingame as TerrarumIngame
|
// val ingame = ingame as TerrarumIngame
|
||||||
if (musicState == 0) musicState = STATE_INTERMISSION
|
if (musicState == 0) musicState = STATE_INTERMISSION
|
||||||
|
if (ambState == 0) ambState = STATE_INTERMISSION
|
||||||
|
|
||||||
|
|
||||||
when (musicState) {
|
when (musicState) {
|
||||||
|
|||||||
@@ -375,9 +375,9 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
|
|
||||||
private val stripW = 56
|
private val stripW = 56
|
||||||
private val stripGap = 1
|
private val stripGap = 1
|
||||||
private val stripFilterHeight = 32
|
private val stripFilterHeight = 16
|
||||||
private val stripFaderHeight = 200
|
private val stripFaderHeight = 200
|
||||||
private val numberOfFilters = 5
|
private val numberOfFilters = 10
|
||||||
private val stripH = stripFaderHeight + stripFilterHeight * numberOfFilters + 16
|
private val stripH = stripFaderHeight + stripFilterHeight * numberOfFilters + 16
|
||||||
|
|
||||||
private val COL_WELL = Color(0x374854_aa)
|
private val COL_WELL = Color(0x374854_aa)
|
||||||
@@ -443,17 +443,20 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
batch.color = COL_FILTER_WELL_BACK
|
batch.color = COL_FILTER_WELL_BACK
|
||||||
Toolkit.fillArea(batch, x, y, stripW, stripFilterHeight * numberOfFilters)
|
Toolkit.fillArea(batch, x, y, stripW, stripFilterHeight * numberOfFilters)
|
||||||
|
|
||||||
|
var filterBankYcursor = 0
|
||||||
track.filters.forEachIndexed { i, filter -> if (filter !is NullFilter) {
|
track.filters.forEachIndexed { i, filter -> if (filter !is NullFilter) {
|
||||||
// draw filter title back
|
// draw filter title back
|
||||||
batch.color = COL_FILTER_TITLE_SHADE
|
batch.color = COL_FILTER_TITLE_SHADE
|
||||||
Toolkit.fillArea(batch, x, y + stripFilterHeight * i, stripW, 16)
|
Toolkit.fillArea(batch, x, y + filterBankYcursor, stripW, 16)
|
||||||
batch.color = COL_FILTER_TITLE
|
batch.color = COL_FILTER_TITLE
|
||||||
Toolkit.fillArea(batch, x, y + stripFilterHeight * i, stripW, 14)
|
Toolkit.fillArea(batch, x, y + filterBankYcursor, stripW, 14)
|
||||||
// draw filter name
|
// draw filter name
|
||||||
batch.color = if (filter.bypass) FILTER_BYPASSED else FILTER_NAME_ACTIVE
|
batch.color = if (filter.bypass) FILTER_BYPASSED else FILTER_NAME_ACTIVE
|
||||||
App.fontSmallNumbers.draw(batch, filter.javaClass.simpleName, x + 3f, y + stripFilterHeight * i + 1f)
|
App.fontSmallNumbers.draw(batch, filter.javaClass.simpleName, x + 3f, y + filterBankYcursor + 1f)
|
||||||
|
|
||||||
drawFilterParam(batch, x, y + stripFilterHeight * i + 16, filter, track)
|
drawFilterParam(batch, x, y + filterBankYcursor + stripFilterHeight, filter, track)
|
||||||
|
|
||||||
|
filterBankYcursor += stripFilterHeight + paramViewHeight.getOrDefault(filter.javaClass.simpleName, 0)
|
||||||
} }
|
} }
|
||||||
|
|
||||||
val faderY = y + stripFilterHeight * numberOfFilters
|
val faderY = y + stripFilterHeight * numberOfFilters
|
||||||
@@ -537,6 +540,11 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val paramViewHeight = hashMapOf(
|
||||||
|
"Lowpass" to 16,
|
||||||
|
"Buffer" to 32,
|
||||||
|
)
|
||||||
|
|
||||||
private fun drawFilterParam(batch: SpriteBatch, x: Int, y: Int, filter: TerrarumAudioFilter, track: TerrarumAudioMixerTrack) {
|
private fun drawFilterParam(batch: SpriteBatch, x: Int, y: Int, filter: TerrarumAudioFilter, track: TerrarumAudioMixerTrack) {
|
||||||
when (filter) {
|
when (filter) {
|
||||||
is Lowpass -> {
|
is Lowpass -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user