From a4cb3f4d298525c215dc562013663e028a43153a Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 28 Dec 2023 19:06:29 +0900 Subject: [PATCH] working gapless playback but musicplayer anim is not --- .../terrarum/musicplayer/gui/MusicPlayer.kt | 15 +++++++ .../terrarum/audio/MixerTrackProcessor.kt | 25 ++++++++--- .../terrarum/audio/TerrarumAudioMixerTrack.kt | 13 ++++-- .../modulebasegame/TerrarumMusicGovernor.kt | 41 +++++++++++-------- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayer.kt b/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayer.kt index 3ba2af552..1a6543921 100644 --- a/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayer.kt +++ b/MusicPlayer/src/net/torvald/terrarum/musicplayer/gui/MusicPlayer.kt @@ -86,6 +86,18 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() { val shuffled = playlistFile.get("shuffled").asBoolean() val fileToName = playlistFile.get("titles") + + AudioMixer.musicTrack.let { track -> + track.doGaplessPlayback = (diskJockeyingMode == "continuous") + if (track.doGaplessPlayback) { + track.pullNextTrack = { + track.currentTrack = ingame.musicGovernor.pullNextMusicTrack() + setMusicName(track.currentTrack?.name ?: "") + } + } + } + + registerPlaylist(albumDir, fileToName, shuffled, diskJockeyingMode) } @@ -109,6 +121,9 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() { setIntermission() transitionRequest = MODE_IDLE } + else if (diskJockeyingMode == "continuous") { + + } } } diff --git a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt index 81cae1cd6..806add743 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -45,12 +45,27 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) { printdbg("Allocating a StreamBuf with rate ${track.currentTrack!!.samplingRate}") - streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { - val l = track.currentTrack?.gdxMusic?.forceInvoke("read", arrayOf(it)) - track.currentTrack?.let { - it.samplesRead += l!! / 4 + streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { buffer -> + var bytesRead = track.currentTrack?.gdxMusic?.forceInvoke("read", arrayOf(buffer)) ?: 0 + + // increment samplesRead of the current track + track.currentTrack?.let { it.samplesRead += bytesRead / 4 } + + // do gapless fetch if there is space in the buffer + if (track.doGaplessPlayback && bytesRead < buffer.size) { + track.currentTrack?.reset() + track.pullNextTrack() + + val tmpBuf = ByteArray(buffer.size - bytesRead) + val newRead = track.currentTrack?.gdxMusic?.forceInvoke("read", arrayOf(tmpBuf)) ?: 0 + + track.currentTrack?.let { it.samplesRead += newRead / 4 } + System.arraycopy(tmpBuf, 0, buffer, bytesRead, tmpBuf.size) + + bytesRead += newRead } - l + + bytesRead }, { track.stop() this.streamBuf = null diff --git a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt index f7ec0a4ea..bfc643c8a 100644 --- a/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt +++ b/src/net/torvald/terrarum/audio/TerrarumAudioMixerTrack.kt @@ -22,7 +22,14 @@ enum class TrackType { STATIC_SOURCE, DYNAMIC_SOURCE, BUS, MASTER } -class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, var maxVolumeFun: () -> Double = {1.0}): Disposable { +class TerrarumAudioMixerTrack( + val name: String, + val trackType: TrackType, + var doGaplessPlayback: Boolean = false, // if true, the audio will be pulled from the `nextTrack` to always fully fill the read-buffer + var maxVolumeFun: () -> Double = {1.0} +): Disposable { + + var pullNextTrack = {} companion object { const val SAMPLING_RATE = 48000 @@ -138,8 +145,8 @@ class TerrarumAudioMixerTrack(val name: String, val trackType: TrackType, var ma override fun equals(other: Any?) = this.hash == (other as TerrarumAudioMixerTrack).hash fun stop() { - currentTrack?.samplesRead = 0L - currentTrack?.gdxMusic?.forceInvoke("reset", arrayOf()) + currentTrack?.reset() + streamPlaying = false // playStartedTime = 0L diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumMusicGovernor.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumMusicGovernor.kt index 063992029..f792b9eca 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumMusicGovernor.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumMusicGovernor.kt @@ -11,6 +11,7 @@ import com.badlogic.gdx.utils.GdxRuntimeException import com.jcraft.jorbis.VorbisFile import javazoom.jl.decoder.Bitstream import net.torvald.reflection.extortField +import net.torvald.reflection.forceInvoke import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.audio.AudioMixer @@ -129,6 +130,11 @@ data class MusicContainer( } override fun toString() = if (name.isEmpty()) file.nameWithoutExtension else name + + fun reset() { + samplesRead = 0L + gdxMusic.forceInvoke("reset", arrayOf()) + } } class TerrarumMusicGovernor : MusicGovernor() { @@ -270,6 +276,23 @@ class TerrarumMusicGovernor : MusicGovernor() { musicState = STATE_PLAYING } + // MixerTrackProcessor will call this function externally to make gapless playback work + fun pullNextMusicTrack(): MusicContainer { + // prevent same song to play twice in row (for the most time) + if (musicBin.isEmpty()) { + restockMUsicBin() + } + return songs[musicBin.removeAt(0)] + } + + // MixerTrackProcessor will call this function externally to make gapless playback work + fun pullNextAmbientTrack(): MusicContainer { + // prevent same song to play twice in row (for the most time) + if (ambientsBin.isEmpty()) { + ambientsBin = ArrayList(ambients.indices.toList().shuffled()) + } + return ambients[ambientsBin.removeAt(0)] + } private fun stopAmbient() { ambState = STATE_INTERMISSION @@ -297,14 +320,7 @@ class TerrarumMusicGovernor : MusicGovernor() { STATE_FIREPLAY -> { if (!musicFired) { musicFired = true - - // prevent same song to play twice - if (musicBin.isEmpty()) { - restockMUsicBin() - } - val song = songs[musicBin.removeAt(0)] - - startMusic(song) + startMusic(pullNextMusicTrack()) } } STATE_PLAYING -> { @@ -324,14 +340,7 @@ class TerrarumMusicGovernor : MusicGovernor() { STATE_FIREPLAY -> { if (!ambFired) { ambFired = true - - // prevent same song to play twice - if (ambientsBin.isEmpty()) { - ambientsBin = ArrayList(ambients.indices.toList().shuffled()) - } - val song = ambients[ambientsBin.removeAt(0)] - - startAmbient(song) + startAmbient(pullNextAmbientTrack()) } } STATE_PLAYING -> {