diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index d9a2c3b65..276e6c362 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -951,6 +951,23 @@ fun distBetweenActors(a: ActorWithBody, b: ActorWithBody): Double { val dist = min(min(bpos.distanceSquared(apos1), bpos.distanceSquared(apos2)), bpos.distanceSquared(apos3)) return dist.sqrt() } + +/** + * @return positive if the otherActor is on the right side of the player, negative if on the left + */ +fun relativeXposition(thisActor: ActorWithBody, otherActor: ActorWithBody): Double { + val ww = INGAME.world.width * TILE_SIZED + val thisPos = thisActor.centrePosVector + val pos1 = otherActor.centrePosVector + val pos2 = Vector2(pos1.x + ww, pos1.y) + val pos3 = Vector2(pos1.x - ww, pos1.y) + val posToUse = listOf( + pos1 to thisPos.distanceSquared(pos1), + pos2 to thisPos.distanceSquared(pos2), + pos3 to thisPos.distanceSquared(pos3), + ).sortedBy { it.second }.first().first + return posToUse.x - thisPos.x +} fun distBetween(a: ActorWithBody, bpos: Vector2): Double { val ww = INGAME.world.width * TILE_SIZED val apos1 = a.centrePosVector diff --git a/src/net/torvald/terrarum/audio/AudioMixer.kt b/src/net/torvald/terrarum/audio/AudioMixer.kt index fddc2034e..27593dbb8 100644 --- a/src/net/torvald/terrarum/audio/AudioMixer.kt +++ b/src/net/torvald/terrarum/audio/AudioMixer.kt @@ -95,6 +95,26 @@ object AudioMixer: Disposable { var processing = true + var actorNowPlaying = Terrarum.ingame?.actorNowPlaying; private set + + /** + * Return oldest dynamic track, even if the track is currently playing + */ + fun getFreeTrackNoMatterWhat(): TerrarumAudioMixerTrack { + return 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 + } + private val processingExecutor = ThreadExecutor() // private lateinit var processingSubthreads: List val processingThread = Thread { @@ -117,6 +137,8 @@ object AudioMixer: Disposable { }*/ while (processing) { + actorNowPlaying = Terrarum.ingame?.actorNowPlaying + for (tracks in parallelProcessingSchedule) { if (!processing) break diff --git a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt index bb14a1731..a2d2fe4ce 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -2,10 +2,13 @@ package net.torvald.terrarum.audio import com.badlogic.gdx.utils.Queue import net.torvald.reflection.forceInvoke +import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.audio.dsp.BinoPan import net.torvald.terrarum.audio.dsp.NullFilter +import net.torvald.terrarum.gameactors.ActorWithBody +import net.torvald.terrarum.relativeXposition import net.torvald.terrarum.sqr -import kotlin.math.absoluteValue -import kotlin.math.sqrt +import kotlin.math.* /** * Created by minjaesong on 2023-11-17. @@ -35,6 +38,8 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru private var breakBomb = false + private val distFalloff = 2048.0 + private fun printdbg(msg: Any) { if (true) println("[AudioAdapter ${track.name}] $msg") } @@ -67,15 +72,30 @@ class MixerTrackProcessor(val bufferSize: Int, val rate: Int, val track: Terraru if (breakBomb) break*/ // uncomment to multithread // Your code here + + // update panning and shits + if (track.trackType == TrackType.DYNAMIC_SOURCE && track.isPlaying) { + if (AudioMixer.actorNowPlaying != null) { + if (track.trackingTarget == null || track.trackingTarget == AudioMixer.actorNowPlaying) { + track.volume = track.maxVolume + (track.filters[0] as BinoPan).pan = 0f + } + else { + val relativeXpos = relativeXposition(AudioMixer.actorNowPlaying!!, track.trackingTarget!!) + 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() + } + } + } + + // fetch deviceBufferSize amount of sample from the disk if (track.trackType != TrackType.MASTER && track.trackType != TrackType.BUS && track.streamPlaying) { streamBuf.fetchBytes { val bytesRead = track.currentTrack?.gdxMusic?.forceInvoke("read", arrayOf(it)) if (bytesRead == null || bytesRead <= 0) { // some class (namely Mp3) may return 0 instead of negative value // printdbg("Finished reading audio stream") - track.currentTrack?.gdxMusic?.forceInvoke("reset", arrayOf()) - track.streamPlaying = false - track.fireSongFinishHook() + track.stop() } } diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt index ca46cd545..085c2ec9a 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt @@ -1,12 +1,15 @@ package net.torvald.terrarum.audio import com.badlogic.gdx.Gdx +import com.badlogic.gdx.audio.Sound import com.badlogic.gdx.backends.lwjgl3.audio.OpenALLwjgl3Audio import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Queue +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.ActorWithBody import net.torvald.terrarum.getHashStr import net.torvald.terrarum.hashStrMap import net.torvald.terrarum.modulebasegame.MusicContainer @@ -36,6 +39,8 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, privat var currentTrack: MusicContainer? = null var nextTrack: MusicContainer? = null + var currentSound: Sound? = null // DYNAMIC_SOURCE only + var volume: TrackVolume = 1.0 get() = field set(value) { @@ -46,14 +51,16 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, privat val maxVolume: Double get() = maxVolumeFun() - var pan = 0.0 - var dBfs: Double get() = fullscaleToDecibels(volume) set(value) { volume = decibelsToFullscale(value) } val filters: Array = Array(4) { NullFilter } + var trackingTarget: ActorWithBody? = null + + var playStartedTime = 0L; private set + inline fun getFilter() = filters.filterIsInstance().first()!! internal val sidechainInputs = ArrayList?>() @@ -110,6 +117,7 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, privat internal var streamPlaying = false fun play() { + playStartedTime = System.nanoTime() streamPlaying = true // currentTrack?.gdxMusic?.play() } @@ -129,6 +137,14 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, privat override fun equals(other: Any?) = this.hash == (other as TerrarumAudioMixerTrack).hash + fun stop() { + currentTrack?.gdxMusic?.forceInvoke("reset", arrayOf()) + streamPlaying = false + playStartedTime = 0L + fireSongFinishHook() + // fireSoundFinishHook() + } + fun fireSongFinishHook() { currentTrack?.songFinishedHook?.invoke(currentTrack!!.gdxMusic) } diff --git a/src/net/torvald/terrarum/gameactors/Actor.kt b/src/net/torvald/terrarum/gameactors/Actor.kt index d8e681af5..59e84b669 100644 --- a/src/net/torvald/terrarum/gameactors/Actor.kt +++ b/src/net/torvald/terrarum/gameactors/Actor.kt @@ -1,9 +1,14 @@ 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.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.modulebasegame.MusicContainer import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.Pocketed import net.torvald.terrarum.savegame.toBigEndian @@ -88,6 +93,60 @@ abstract class Actor : Comparable, Runnable { */ abstract @Event fun onActorValueChange(key: String, value: Any?) +// @Transient val soundTracks = HashMap() + @Transient val musicTracks = HashMap() + + /*open fun startAudio(sound: Sound) { + getTrackByAudio(sound)?.let { + it.trackingTarget = if (this is ActorWithBody) this else null + it.currentSound = sound + it.play() + } + }*/ + + private fun getTrackByAudio(music: MusicContainer): TerrarumAudioMixerTrack? { + // get existing track + var track = musicTracks[music] + + // if there is no existing track, try to get one + if (track == null) { + track = if (this == Terrarum.ingame?.actorNowPlaying) + AudioMixer.getFreeTrackNoMatterWhat() + else + AudioMixer.getFreeTrack() + // if the request was successful, put it into the hashmap + if (track != null) { + musicTracks[music] = track + } + } + + return track + } + + open fun startAudio(music: MusicContainer) { + getTrackByAudio(music)?.let { + it.trackingTarget = if (this is ActorWithBody) this else null + it.currentTrack = music + it.play() + } + } + + /*open fun stopAudio(sound: Sound) { + + }*/ + + open fun stopAudio(music: MusicContainer) { + musicTracks[music]?.stop() + } + + /*open fun onAudioInterrupt(sound: Sound) { + + }*/ + + open @Event fun onAudioInterrupt(music: MusicContainer) { + music.songFinishedHook(music.gdxMusic) + } + abstract fun dispose() @Transient val localHash = HQRNG().nextInt()