working gapless playback but musicplayer anim is not

This commit is contained in:
minjaesong
2023-12-28 19:06:29 +09:00
parent 3e7434a9c6
commit a4cb3f4d29
4 changed files with 70 additions and 24 deletions

View File

@@ -86,6 +86,18 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
val shuffled = playlistFile.get("shuffled").asBoolean() val shuffled = playlistFile.get("shuffled").asBoolean()
val fileToName = playlistFile.get("titles") 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) registerPlaylist(albumDir, fileToName, shuffled, diskJockeyingMode)
} }
@@ -109,6 +121,9 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
setIntermission() setIntermission()
transitionRequest = MODE_IDLE transitionRequest = MODE_IDLE
} }
else if (diskJockeyingMode == "continuous") {
}
} }
} }

View File

@@ -45,12 +45,27 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra
private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) { private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) {
printdbg("Allocating a StreamBuf with rate ${track.currentTrack!!.samplingRate}") printdbg("Allocating a StreamBuf with rate ${track.currentTrack!!.samplingRate}")
streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { buffer ->
val l = track.currentTrack?.gdxMusic?.forceInvoke<Int>("read", arrayOf(it)) var bytesRead = track.currentTrack?.gdxMusic?.forceInvoke<Int>("read", arrayOf(buffer)) ?: 0
track.currentTrack?.let {
it.samplesRead += l!! / 4 // 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<Int>("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() track.stop()
this.streamBuf = null this.streamBuf = null

View File

@@ -22,7 +22,14 @@ enum class TrackType {
STATIC_SOURCE, DYNAMIC_SOURCE, BUS, MASTER 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 { companion object {
const val SAMPLING_RATE = 48000 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 override fun equals(other: Any?) = this.hash == (other as TerrarumAudioMixerTrack).hash
fun stop() { fun stop() {
currentTrack?.samplesRead = 0L currentTrack?.reset()
currentTrack?.gdxMusic?.forceInvoke<Int>("reset", arrayOf())
streamPlaying = false streamPlaying = false
// playStartedTime = 0L // playStartedTime = 0L

View File

@@ -11,6 +11,7 @@ import com.badlogic.gdx.utils.GdxRuntimeException
import com.jcraft.jorbis.VorbisFile import com.jcraft.jorbis.VorbisFile
import javazoom.jl.decoder.Bitstream import javazoom.jl.decoder.Bitstream
import net.torvald.reflection.extortField import net.torvald.reflection.extortField
import net.torvald.reflection.forceInvoke
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.terrarum.audio.AudioMixer
@@ -129,6 +130,11 @@ data class MusicContainer(
} }
override fun toString() = if (name.isEmpty()) file.nameWithoutExtension else name override fun toString() = if (name.isEmpty()) file.nameWithoutExtension else name
fun reset() {
samplesRead = 0L
gdxMusic.forceInvoke<Int>("reset", arrayOf())
}
} }
class TerrarumMusicGovernor : MusicGovernor() { class TerrarumMusicGovernor : MusicGovernor() {
@@ -270,6 +276,23 @@ class TerrarumMusicGovernor : MusicGovernor() {
musicState = STATE_PLAYING 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() { private fun stopAmbient() {
ambState = STATE_INTERMISSION ambState = STATE_INTERMISSION
@@ -297,14 +320,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
STATE_FIREPLAY -> { STATE_FIREPLAY -> {
if (!musicFired) { if (!musicFired) {
musicFired = true musicFired = true
startMusic(pullNextMusicTrack())
// prevent same song to play twice
if (musicBin.isEmpty()) {
restockMUsicBin()
}
val song = songs[musicBin.removeAt(0)]
startMusic(song)
} }
} }
STATE_PLAYING -> { STATE_PLAYING -> {
@@ -324,14 +340,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
STATE_FIREPLAY -> { STATE_FIREPLAY -> {
if (!ambFired) { if (!ambFired) {
ambFired = true ambFired = true
startAmbient(pullNextAmbientTrack())
// prevent same song to play twice
if (ambientsBin.isEmpty()) {
ambientsBin = ArrayList(ambients.indices.toList().shuffled())
}
val song = ambients[ambientsBin.removeAt(0)]
startAmbient(song)
} }
} }
STATE_PLAYING -> { STATE_PLAYING -> {