footsteps using dynamic track wip

This commit is contained in:
minjaesong
2023-12-02 22:45:26 +09:00
parent 6da28b943a
commit 18f8fa881f
41 changed files with 225 additions and 17 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -261,7 +261,20 @@ $BULLET Ambient sound recordings:
- crickets_02.ogg
Copyright (C) 2012, 2013, 2015, 2016, 2017 Klankbeeld
Sound from <https://www.freesound.org/people/klankbeeld/>
Sound from <https://www.freesound.org/people/klankbeeld>
$BULLET Footstep sound recordings:
- GRSS_01..06.ogg
- GRVL_01..07.ogg
- ROCK_01..09.ogg
- SAND_01..04.ogg
- WOOD_01..03.ogg
Copyright (C) 2020, 2021, 2022 Nox Sound
Sound from <https://freesound.org/people/Nox_Sound>

View File

@@ -217,7 +217,7 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
actorContainerInactive.forEach { it.dispose() }
world.dispose()
disposables.forEach(Consumer { it.tryDispose() })
disposables.forEach { it.tryDispose() }
}
////////////

View File

@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.utils.JsonValue
import net.torvald.terrarum.App.*
import net.torvald.terrarum.App.setToGameConfig
import net.torvald.terrarum.audio.AudioCodex
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.OreCodex
import net.torvald.terrarum.blockproperties.WireCodex
@@ -464,6 +465,16 @@ object ModMgr {
return dir.listFiles()
}
}
fun getGdxFiles(module: String, path: String): Array<FileHandle> {
checkExistence(module)
val dir = getGdxFile(module, path)
if (!dir.isDirectory) {
throw FileNotFoundException("The path is not a directory")
}
else {
return dir.list()
}
}
/** Get a common file (literal file or directory) from all the installed mods. Files are guaranteed to exist. If a mod does not
* contain the file, the mod will be skipped.
@@ -668,6 +679,35 @@ object ModMgr {
}
}
object GameAudioLoader {
val audioPath = listOf(
"audio/music",
"audio/effects",
"audio/ambient",
)
init {
Terrarum.audioCodex = AudioCodex()
}
private fun loadAudio(basename: String, file: FileHandle) {
if (file.isDirectory)
file.list().forEach { loadAudio("$basename.${it.nameWithoutExtension()}", it) }
else {
val id = basename
val materialID = file.nameWithoutExtension().substringBefore('_')
Terrarum.audioCodex.addToFootstepPool(materialID, file)
printdbg(this, "Registering music $id")
}
}
@JvmStatic operator fun invoke(module: String) {
audioPath.forEach {
getGdxFiles(module, it).forEach { file -> loadAudio("audio.${file.name()}", file) }
}
}
}
/**
* A sugar-library for easy texture pack creation
*/

View File

@@ -16,6 +16,7 @@ import net.torvald.random.HQRNG
import net.torvald.terrarum.App.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.audio.AudioCodex
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.FluidCodex
import net.torvald.terrarum.blockproperties.OreCodex
@@ -78,6 +79,7 @@ object Terrarum : Disposable {
var apocryphas = HashMap<String, Any>(); internal set
var fluidCodex = FluidCodex(); internal set
var oreCodex = OreCodex(); internal set
var audioCodex = AudioCodex(); internal set
//////////////////////////////

View File

@@ -0,0 +1,34 @@
package net.torvald.terrarum.audio
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.audio.Music
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.tryDispose
typealias MaterialID = String
/**
* Created by minjaesong on 2023-12-02.
*/
class AudioCodex: Disposable {
@Transient val footsteps = HashMap<MaterialID, HashSet<MusicContainer>>()
internal constructor()
fun addToFootstepPool(materialID: MaterialID, music: FileHandle) {
if (footsteps[materialID] == null) footsteps[materialID] = HashSet()
footsteps[materialID]!!.add(
MusicContainer(music.nameWithoutExtension(), music.file(), Gdx.audio.newMusic(music)) {}
)
}
fun getRandomFootstep(materialID: MaterialID) = footsteps[materialID]?.random()
override fun dispose() {
footsteps.values.forEach { it.forEach { it.gdxMusic.tryDispose() } }
}
}

View File

@@ -101,18 +101,14 @@ object AudioMixer: Disposable {
* Return oldest dynamic track, even if the track is currently playing
*/
fun getFreeTrackNoMatterWhat(): TerrarumAudioMixerTrack {
return dynamicTracks.minBy { it.playStartedTime }
return getFreeTrack() ?: dynamicTracks.minBy { it.playStartedTime }
}
/**
* Return oldest dynamic track that is not playing
*/
fun getFreeTrack(): TerrarumAudioMixerTrack? {
val oldestTrack = dynamicTracks.minBy { it.playStartedTime }
return if (oldestTrack.isPlaying)
null
else
oldestTrack
return dynamicTracks.filter { it.trackingTarget == null && !it.isPlaying }.minByOrNull { it.playStartedTime }
}
private val processingExecutor = ThreadExecutor()

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.audio
import com.badlogic.gdx.utils.Queue
import net.torvald.reflection.forceInvoke
import net.torvald.terrarum.App
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.audio.dsp.BinoPan
import net.torvald.terrarum.audio.dsp.NullFilter
@@ -41,7 +42,7 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
private val distFalloff = 2048.0
private fun printdbg(msg: Any) {
if (true) println("[AudioAdapter ${track.name}] $msg")
if (true) App.printdbg("AudioAdapter ${track.name}", msg)
}
override fun run() {
// while (running) { // uncomment to multithread
@@ -80,8 +81,8 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
track.volume = track.maxVolume
(track.filters[0] as BinoPan).pan = 0f
}
else {
val relativeXpos = relativeXposition(AudioMixer.actorNowPlaying!!, track.trackingTarget!!)
else if (track.trackingTarget is ActorWithBody) {
val relativeXpos = relativeXposition(AudioMixer.actorNowPlaying!!, track.trackingTarget as ActorWithBody)
track.volume = track.maxVolume * (1.0 - relativeXpos.absoluteValue.pow(0.5) / distFalloff)
(track.filters[0] as BinoPan).pan = ((2*asin(relativeXpos / distFalloff)) / Math.PI).toFloat()
}
@@ -91,6 +92,10 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru
// fetch deviceBufferSize amount of sample from the disk
if (track.trackType != TrackType.MASTER && track.trackType != TrackType.BUS && track.streamPlaying) {
if (track.trackType == TrackType.DYNAMIC_SOURCE) {
printdbg("${track.name} streaming")
}
streamBuf.fetchBytes {
val bytesRead = track.currentTrack?.gdxMusic?.forceInvoke<Int>("read", arrayOf(it))
if (bytesRead == null || bytesRead <= 0) { // some class (namely Mp3) may return 0 instead of negative value

View File

@@ -9,6 +9,7 @@ import net.torvald.reflection.forceInvoke
import net.torvald.terrarum.App
import net.torvald.terrarum.audio.dsp.NullFilter
import net.torvald.terrarum.audio.dsp.TerrarumAudioFilter
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.getHashStr
import net.torvald.terrarum.hashStrMap
@@ -22,7 +23,7 @@ enum class TrackType {
STATIC_SOURCE, DYNAMIC_SOURCE, BUS, MASTER
}
class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, private val maxVolumeFun: () -> Double = {1.0}): Disposable {
class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, var maxVolumeFun: () -> Double = {1.0}): Disposable {
companion object {
const val SAMPLING_RATE = 48000
@@ -57,7 +58,7 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, privat
val filters: Array<TerrarumAudioFilter> = Array(4) { NullFilter }
var trackingTarget: ActorWithBody? = null
var trackingTarget: Actor? = null
var playStartedTime = 0L; private set
@@ -140,9 +141,16 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, privat
fun stop() {
currentTrack?.gdxMusic?.forceInvoke<Int>("reset", arrayOf())
streamPlaying = false
playStartedTime = 0L
// playStartedTime = 0L
if (trackingTarget != null && currentTrack != null) {
trackingTarget!!.onAudioInterrupt(currentTrack!!)
}
fireSongFinishHook()
// fireSoundFinishHook()
trackingTarget = null
}
fun fireSongFinishHook() {

View File

@@ -3,11 +3,13 @@ package net.torvald.terrarum.gameactors
import com.badlogic.gdx.audio.Music
import com.badlogic.gdx.audio.Sound
import net.torvald.random.HQRNG
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.ReferencingRanges
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.audio.AudioMixer
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
import net.torvald.terrarum.audio.TrackVolume
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
@@ -120,13 +122,17 @@ abstract class Actor : Comparable<Actor>, Runnable {
}
}
printdbg(this, "Dynamic Source ${track?.name}")
return track
}
open fun startAudio(music: MusicContainer) {
open fun startAudio(music: MusicContainer, volume: TrackVolume = 1.0) {
getTrackByAudio(music)?.let {
it.trackingTarget = if (this is ActorWithBody) this else null
it.stop()
it.trackingTarget = this
it.currentTrack = music
it.maxVolumeFun = { volume }
it.play()
}
}

View File

@@ -11,6 +11,7 @@ import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF
import net.torvald.terrarum.audio.AudioCodex
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockProp
import net.torvald.terrarum.gamecontroller.KeyToggler
@@ -2041,6 +2042,20 @@ open class ActorWithBody : Actor {
makeDust0(tile, px, py, particleCount, collisionDamage, vecSum)
}
}
if (particleCount >= 1.0) {
val volumeMax = (particleCount.pow(0.75) / 8.0).coerceIn(0.0, 2.0)
val feetTileMats = feetTiles.map { BlockCodex[it.second].material }
val feetTileCnt = feetTileMats.size.toDouble()
val materialStats = feetTileMats.distinct().map { mat -> mat to feetTileMats.count { it == mat } }
materialStats.forEach { (mat, cnt) ->
Terrarum.audioCodex.getRandomFootstep(mat)?.let {
val vol = volumeMax * (cnt / feetTileCnt)
startAudio(it, vol)
printdbg(this, "Playing footstep $mat (vol: $vol, file: ${it.file.name})")
}
}
}
}
}

View File

@@ -43,6 +43,7 @@ class EntryPoint : ModuleEntryPoint() {
ModMgr.GameOreLoader.invoke(moduleName)
ModMgr.GameLanguageLoader.invoke(moduleName)
ModMgr.GameCraftingRecipeLoader.invoke(moduleName)
ModMgr.GameAudioLoader.invoke(moduleName)
if (App.IS_DEVELOPMENT_BUILD) {
println("[EntryPoint] Crafting Recipes: ")

View File

@@ -8,6 +8,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import net.torvald.terrarum.*
import net.torvald.terrarum.App.*
import net.torvald.terrarum.Terrarum.audioCodex
import net.torvald.terrarum.Terrarum.getPlayerSaveFiledesc
import net.torvald.terrarum.Terrarum.getWorldSaveFiledesc
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
@@ -1586,7 +1587,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
musicGovernor.dispose()
audioCodex.dispose()
super.dispose()
}
}