audio mixer thingy wip

This commit is contained in:
minjaesong
2023-11-17 14:22:53 +09:00
parent 21ab607298
commit 29cc69519e
6 changed files with 117 additions and 41 deletions

View File

@@ -2,7 +2,6 @@ package net.torvald.terrarum;
import com.badlogic.gdx.*; import com.badlogic.gdx.*;
import com.badlogic.gdx.audio.AudioDevice; import com.badlogic.gdx.audio.AudioDevice;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.badlogic.gdx.backends.lwjgl3.TerrarumLwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.TerrarumLwjgl3Application;
import com.badlogic.gdx.controllers.Controllers; import com.badlogic.gdx.controllers.Controllers;
@@ -16,6 +15,7 @@ import com.badlogic.gdx.utils.JsonValue;
import com.github.strikerx3.jxinput.XInputDevice; import com.github.strikerx3.jxinput.XInputDevice;
import kotlin.text.Charsets; import kotlin.text.Charsets;
import net.torvald.getcpuname.GetCpuName; import net.torvald.getcpuname.GetCpuName;
import net.torvald.terrarum.audio.AudioMixer;
import net.torvald.terrarum.controller.GdxControllerAdapter; import net.torvald.terrarum.controller.GdxControllerAdapter;
import net.torvald.terrarum.controller.TerrarumController; import net.torvald.terrarum.controller.TerrarumController;
import net.torvald.terrarum.controller.XinputControllerAdapter; import net.torvald.terrarum.controller.XinputControllerAdapter;
@@ -1188,7 +1188,7 @@ public class App implements ApplicationListener {
} }
AudioManager.INSTANCE.getMasterVolume(); AudioMixer.INSTANCE.getMasterVolume();
audioManagerThread = new Thread(new AudioManagerRunnable(), "TerrarumAudioManager"); audioManagerThread = new Thread(new AudioManagerRunnable(), "TerrarumAudioManager");
audioManagerThread.start(); audioManagerThread.start();

View File

@@ -1,7 +1,6 @@
package net.torvald.terrarum package net.torvald.terrarum
import com.badlogic.gdx.Gdx import net.torvald.terrarum.audio.AudioMixer
import net.torvald.terrarum.gamecontroller.InputStrober
/** /**
* Created by minjaesong on 2023-11-08. * Created by minjaesong on 2023-11-08.
@@ -17,7 +16,7 @@ class AudioManagerRunnable : Runnable {
val T = System.nanoTime() val T = System.nanoTime()
dT = (T - oldT) / 1000000000f dT = (T - oldT) / 1000000000f
oldT = T; oldT = T;
AudioManager.update(dT) AudioMixer.update(dT)
// println("AudioManagerRunnable dT = ${dT * 1000f} ms") // println("AudioManagerRunnable dT = ${dT * 1000f} ms")
Thread.sleep(30L) Thread.sleep(30L)
} }

View File

@@ -1,8 +1,8 @@
package net.torvald.terrarum package net.torvald.terrarum.audio
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.backends.lwjgl3.audio.Lwjgl3Audio import com.badlogic.gdx.backends.lwjgl3.audio.Lwjgl3Audio
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App
import net.torvald.terrarum.modulebasegame.MusicContainer import net.torvald.terrarum.modulebasegame.MusicContainer
/** /**
@@ -10,48 +10,58 @@ import net.torvald.terrarum.modulebasegame.MusicContainer
* *
* Created by minjaesong on 2023-11-07. * Created by minjaesong on 2023-11-07.
*/ */
object AudioManager { object AudioMixer {
const val DEFAULT_FADEOUT_LEN = 2.4f const val DEFAULT_FADEOUT_LEN = 2.4
/** Returns a master volume */ /** Returns a master volume */
val masterVolume: Float val masterVolume: Double
get() = App.getConfigDouble("mastervolume").toFloat() get() = App.getConfigDouble("mastervolume")
/** Returns a (master volume * bgm volume) */ /** Returns a (master volume * bgm volume) */
val musicVolume: Float val musicVolume: Double
get() = (App.getConfigDouble("bgmvolume") * App.getConfigDouble("mastervolume")).toFloat() get() = (App.getConfigDouble("bgmvolume") * App.getConfigDouble("mastervolume"))
/** Returns a (master volume * sfx volume */ /** Returns a (master volume * sfx volume */
val ambientVolume: Float val ambientVolume: Double
get() = (App.getConfigDouble("sfxvolume") * App.getConfigDouble("mastervolume")).toFloat() get() = (App.getConfigDouble("sfxvolume") * App.getConfigDouble("mastervolume"))
var currentMusic: MusicContainer? = null private val tracks = Array(10) { TerrarumAudioMixerTracks() }
var currentAmbient: MusicContainer? = null
private var nextMusic: MusicContainer? = null private val masterTrack = TerrarumAudioMixerTracks().also { master ->
tracks.forEach { master.sidechainInputs.add(it to 1.0) }
}
private var fadeAkku = 0f private val musicTrack: TerrarumAudioMixerTracks
get() = tracks[0]
private val ambientTrack: TerrarumAudioMixerTracks
get() = tracks[1]
private var fadeAkku = 0.0
private var fadeLength = DEFAULT_FADEOUT_LEN private var fadeLength = DEFAULT_FADEOUT_LEN
private var fadeoutFired = false private var fadeoutFired = false
private var fadeinFired = false private var fadeinFired = false
// TODO make sidechaining work
// TODO master volume controls the master track
// TODO fadein/out controls the master track
fun update(delta: Float) { fun update(delta: Float) {
(Gdx.audio as? Lwjgl3Audio)?.update() (Gdx.audio as? Lwjgl3Audio)?.update()
if (fadeoutFired) { if (fadeoutFired) {
fadeAkku += delta fadeAkku += delta
currentMusic?.gdxMusic?.volume = (musicVolume * (1f - (fadeAkku / fadeLength))).coerceIn(0f, 1f) musicTrack.volume = (musicVolume * (1.0 - (fadeAkku / fadeLength))).coerceIn(0.0, 1.0)
// printdbg(this, "Fadeout fired - akku: $fadeAkku; volume: ${currentMusic?.gdxMusic?.volume}") // printdbg(this, "Fadeout fired - akku: $fadeAkku; volume: ${currentMusic?.gdxMusic?.volume}")
if (fadeAkku >= fadeLength) { if (fadeAkku >= fadeLength) {
fadeoutFired = false fadeoutFired = false
currentMusic?.gdxMusic?.volume = 0f musicTrack.volume = 0.0
// currentMusic?.gdxMusic?.pause() // currentMusic?.gdxMusic?.pause()
currentMusic = null musicTrack.currentTrack = null
// printdbg(this, "Fadeout end") // printdbg(this, "Fadeout end")
} }
@@ -59,17 +69,17 @@ object AudioManager {
// process fadein request // process fadein request
else if (fadeinFired) { else if (fadeinFired) {
fadeAkku += delta fadeAkku += delta
currentMusic?.gdxMusic?.volume = (musicVolume * (fadeAkku / fadeLength)).coerceIn(0f, 1f) musicTrack.volume = (musicVolume * (fadeAkku / fadeLength)).coerceIn(0.0, 1.0)
// printdbg(this, "Fadein fired - akku: $fadeAkku; volume: ${currentMusic?.gdxMusic?.volume}") // printdbg(this, "Fadein fired - akku: $fadeAkku; volume: ${currentMusic?.gdxMusic?.volume}")
if (currentMusic?.gdxMusic?.isPlaying == false) { if (musicTrack.isPlaying == false) {
currentMusic?.gdxMusic?.play() musicTrack.play()
// printdbg(this, "Fadein starting music ${currentMusic?.name}") // printdbg(this, "Fadein starting music ${currentMusic?.name}")
} }
if (fadeAkku >= fadeLength) { if (fadeAkku >= fadeLength) {
currentMusic?.gdxMusic?.volume = musicVolume musicTrack.volume = musicVolume
fadeinFired = false fadeinFired = false
// printdbg(this, "Fadein end") // printdbg(this, "Fadein end")
@@ -77,38 +87,37 @@ object AudioManager {
} }
if (currentMusic?.gdxMusic?.isPlaying != true && nextMusic != null) { if (musicTrack.isPlaying != true && musicTrack.nextTrack != null) {
// printdbg(this, "Playing next music: ${nextMusic!!.name}") // printdbg(this, "Playing next music: ${nextMusic!!.name}")
currentMusic = nextMusic musicTrack.queueNext(null)
nextMusic = null musicTrack.volume = musicVolume
currentMusic!!.gdxMusic.volume = musicVolume musicTrack.play()
currentMusic!!.gdxMusic.play()
} }
} }
fun startMusic(song: MusicContainer) { fun startMusic(song: MusicContainer) {
if (currentMusic?.gdxMusic?.isPlaying == true) { if (musicTrack.isPlaying == true) {
requestFadeOut(DEFAULT_FADEOUT_LEN) requestFadeOut(DEFAULT_FADEOUT_LEN)
} }
nextMusic = song musicTrack.nextTrack = song
} }
fun stopMusic() { fun stopMusic() {
requestFadeOut(DEFAULT_FADEOUT_LEN) requestFadeOut(DEFAULT_FADEOUT_LEN)
} }
fun requestFadeOut(length: Float) { fun requestFadeOut(length: Double) {
if (!fadeoutFired) { if (!fadeoutFired) {
fadeLength = length.coerceAtLeast(1f/1024f) fadeLength = length.coerceAtLeast(1.0/1024.0)
fadeAkku = 0f fadeAkku = 0.0
fadeoutFired = true fadeoutFired = true
} }
} }
fun requestFadeIn(length: Float) { fun requestFadeIn(length: Double) {
if (!fadeinFired) { if (!fadeinFired) {
fadeLength = length.coerceAtLeast(1f/1024f) fadeLength = length.coerceAtLeast(1.0/1024.0)
fadeAkku = 0f fadeAkku = 0.0
fadeinFired = true fadeinFired = true
} }
} }

View File

@@ -0,0 +1,12 @@
package net.torvald.terrarum.audio
interface TerrarumAudioFilters {
fun thru(inbufL: FloatArray, inbufR: FloatArray, outbufL: FloatArray, outbufR: FloatArray)
}
object NullFilter: TerrarumAudioFilters {
override fun thru(inbufL: FloatArray, inbufR: FloatArray, outbufL: FloatArray, outbufR: FloatArray) {
System.arraycopy(inbufL, 0, outbufL, 0, inbufL.size)
System.arraycopy(inbufR, 0, outbufR, 0, inbufL.size)
}
}

View File

@@ -0,0 +1,55 @@
package net.torvald.terrarum.audio
import net.torvald.terrarum.getHashStr
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.utils.PasswordBase32
import kotlin.math.log10
import kotlin.math.pow
typealias TrackVolume = Double
class TerrarumAudioMixerTracks {
val hash = getHashStr()
var currentTrack: MusicContainer? = null
var nextTrack: MusicContainer? = null
var volume: TrackVolume = 1.0
get() = field
set(value) {
field = value
currentTrack?.gdxMusic?.volume = volume.toFloat()
}
var pan = 0.0
var dBfs: Double
get() = fullscaleToDecibels(volume)
set(value) { volume = decibelsToFullscale(value) }
val filters = arrayListOf<TerrarumAudioFilters>()
val sidechainInputs = arrayListOf<Pair<TerrarumAudioMixerTracks, TrackVolume>>()
/**
* assign nextTrack to currentTrack, then assign nextNext to nextTrack.
* Whatever is on the currentTrack will be lost.
*/
fun queueNext(nextNext: MusicContainer? = null) {
currentTrack = nextTrack
nextTrack = nextNext
}
fun play() {
currentTrack?.gdxMusic?.play()
}
val isPlaying: Boolean?
get() = currentTrack?.gdxMusic?.isPlaying
override fun equals(other: Any?) = this.hash == (other as TerrarumAudioMixerTracks).hash
}
fun fullscaleToDecibels(fs: Double) = 10.0 * log10(fs)
fun decibelsToFullscale(db: Double) = 10.0.pow(db / 10.0)

View File

@@ -5,6 +5,7 @@ import com.badlogic.gdx.audio.Music
import com.badlogic.gdx.utils.GdxRuntimeException import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.audio.AudioMixer
import net.torvald.unicode.EMDASH import net.torvald.unicode.EMDASH
import java.io.File import java.io.File
@@ -67,7 +68,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
} }
private fun startMusic(song: MusicContainer) { private fun startMusic(song: MusicContainer) {
AudioManager.startMusic(song) AudioMixer.startMusic(song)
printdbg(this, "Now playing: $song") printdbg(this, "Now playing: $song")
INGAME.sendNotification("Now Playing $EMDASH ${song.name}") INGAME.sendNotification("Now Playing $EMDASH ${song.name}")
state = STATE_PLAYING state = STATE_PLAYING
@@ -117,7 +118,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
} }
override fun dispose() { override fun dispose() {
AudioManager.stopMusic() // explicit call for fade-out when the game instance quits AudioMixer.stopMusic() // explicit call for fade-out when the game instance quits
stopMusic() stopMusic()
} }
} }