fix: continuous album now plays again

This commit is contained in:
minjaesong
2024-07-07 02:53:57 +09:00
parent c211f67e38
commit 63070a19d5
10 changed files with 75 additions and 46 deletions

View File

@@ -709,6 +709,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
// debug codes // debug codes
//// transaction state
if (MusicService.transactionLocked) { if (MusicService.transactionLocked) {
batch.color = Color.RED batch.color = Color.RED
Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "LOCKED", Toolkit.drawWidth, 0, _posY.toInt() + height + 5) Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "LOCKED", Toolkit.drawWidth, 0, _posY.toInt() + height + 5)
@@ -717,11 +718,24 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
batch.color = Color.WHITE batch.color = Color.WHITE
Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "UNLOCKED", Toolkit.drawWidth, 0, _posY.toInt() + height + 5) Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "UNLOCKED", Toolkit.drawWidth, 0, _posY.toInt() + height + 5)
} }
//// MusicService internal state
batch.color = Color.WHITE batch.color = Color.WHITE
val musicState = MusicService.currentPlaybackState.get() val musicState = MusicService.currentPlaybackState.get()
val str = "State: $musicState Wait: ${MusicService.waitAkku.toIntAndFrac(2)}/${MusicService.waitTime}" val str = "State: $musicState Wait: ${MusicService.waitAkku.toIntAndFrac(2)}/${MusicService.waitTime}"
Toolkit.drawTextCentered(batch, App.fontSmallNumbers, str, Toolkit.drawWidth, 0, _posY.toInt() + height + 18) Toolkit.drawTextCentered(batch, App.fontSmallNumbers, str, Toolkit.drawWidth, 0, _posY.toInt() + height + 18)
//// playlist internal indices
MusicService.currentPlaylist?.let {
val indices = it.extortField<ArrayList<Int>>("internalIndices")!!
val currentIndex = it.extortField<Int>("currentIndexCursor")!!
for (k in 0 until indices.size) {
batch.color = if (k == currentIndex) Color.RED else Color.WHITE
App.fontSmallNumbers.draw(batch, "${indices[k]+1}", 28f + 18f * (k), App.scr.hf - 16f)
}
}
batch.color = Color.LIGHT_GRAY
App.fontSmallNumbers.draw(batch, "Playlist InternalIndices", 10f, App.scr.hf - 30f)
App.fontSmallNumbers.draw(batch, "..", 10f, App.scr.hf - 16f)
// end of debug codes // end of debug codes
@@ -1347,16 +1361,6 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
private fun loadNewAlbum(albumDir: File): TerrarumMusicPlaylist { private fun loadNewAlbum(albumDir: File): TerrarumMusicPlaylist {
val albumProp = albumPropCache[albumDir] val albumProp = albumPropCache[albumDir]
App.audioMixer.musicTrack.let { track ->
track.doGaplessPlayback = (albumProp.diskJockeyingMode == "continuous")
if (track.doGaplessPlayback) {
track.pullNextTrack = {
track.currentTrack = MusicService.currentPlaylist!!.queueNext()
setMusicName(track.currentTrack?.name ?: "")
}
}
}
currentlySelectedAlbum = albumProp currentlySelectedAlbum = albumProp
return registerPlaylist(albumDir.absolutePath, albumProp.fileToName, albumProp.shuffled, albumProp.diskJockeyingMode) return registerPlaylist(albumDir.absolutePath, albumProp.fileToName, albumProp.shuffled, albumProp.diskJockeyingMode)

View File

@@ -166,6 +166,7 @@ object MusicService : TransactionListener() {
stopPlaylistPlayback {} stopPlaylistPlayback {}
} }
/** /**
* Puts the given playlist to this object if the transaction successes. If the given playlist is same as the * Puts the given playlist to this object if the transaction successes. If the given playlist is same as the
* current playlist, the transaction will successfully finish immediately; otherwise the given playlist will * current playlist, the transaction will successfully finish immediately; otherwise the given playlist will
@@ -213,11 +214,22 @@ object MusicService : TransactionListener() {
oldPlaylist?.dispose() oldPlaylist?.dispose()
(state["currentPlaylist"] as TerrarumMusicPlaylist?)?.let { (state["currentPlaylist"] as TerrarumMusicPlaylist?)?.let {
// set songFinishedHook for every song
it.musicList.forEach { it.musicList.forEach {
it.songFinishedHook = { it.songFinishedHook = {
onMusicFinishing(it) onMusicFinishing(it)
} }
} }
// set gaplessness of the Music track
App.audioMixer.musicTrack.let { track ->
track.doGaplessPlayback = (it.diskJockeyingMode == "continuous")
if (track.doGaplessPlayback) {
track.pullNextTrack = {
track.currentTrack = MusicService.currentPlaylist!!.queueNext()
}
}
}
} }
onSuccess() onSuccess()
@@ -313,7 +325,9 @@ object MusicService : TransactionListener() {
if (err != null) throw err!! if (err != null) throw err!!
} }
override fun onSuccess(state: TransactionState) { onSuccess() } override fun onSuccess(state: TransactionState) {
onSuccess()
}
override fun onFailure(e: Throwable, state: TransactionState) { override fun onFailure(e: Throwable, state: TransactionState) {
e.printStackTrace() e.printStackTrace()
} }

View File

@@ -26,7 +26,7 @@ class TerrarumMusicPlaylist(
): Disposable { ): Disposable {
private val internalIndices = ArrayList<Int>() private val internalIndices = ArrayList<Int>()
private var currentIndexCursor = musicList.size private var currentIndexCursor = musicList.size - 1
init { init {
reset() reset()
@@ -36,7 +36,7 @@ class TerrarumMusicPlaylist(
internalIndices.clear() internalIndices.clear()
refillInternalIndices() refillInternalIndices()
refillInternalIndices() refillInternalIndices()
currentIndexCursor = musicList.size currentIndexCursor = musicList.size - 1
} }
private fun checkRefill() { private fun checkRefill() {
@@ -50,6 +50,11 @@ class TerrarumMusicPlaylist(
return musicList[internalIndices[currentIndexCursor]] return musicList[internalIndices[currentIndexCursor]]
} }
/**
* For Gapless playback, this function is called by track's pullNextTrack callback (defined in [MusicService.createTransactionPlaylistChange])
*
* For intermittent playback, this function is called by the transaction defined in [MusicService.update]
*/
fun queueNext(): MusicContainer { fun queueNext(): MusicContainer {
checkRefill() checkRefill()
currentIndexCursor += 1 currentIndexCursor += 1
@@ -81,16 +86,12 @@ class TerrarumMusicPlaylist(
fun queueNthSong(n: Int): MusicContainer { fun queueNthSong(n: Int): MusicContainer {
checkRefill() checkRefill()
internalIndices.add(currentIndexCursor, n) internalIndices.add(currentIndexCursor, n)
currentIndexCursor -= 1
return musicList[internalIndices[currentIndexCursor]] return musicList[internalIndices[currentIndexCursor]]
} }
private fun refillInternalIndices() { private fun refillInternalIndices() {
if (diskJockeyingMode == "continuous") { internalIndices.addAll(musicList.indices.toMutableList().also { if (shuffled) it.shuffle() })
internalIndices.add(0) // playlist is a one long track for the gapless playback
}
else {
internalIndices.addAll(musicList.indices.toMutableList().also { if (shuffled) it.shuffle() })
}
} }

View File

@@ -250,7 +250,7 @@ class AudioMixer : Disposable {
// initialise audio paths // // initialise audio paths //
listOf(musicTrack, ambientTrack1, ambientTrack2, ambientTrack3, ambientTrack4, guiTrack).forEach { listOf(musicTrack, ambientTrack1, ambientTrack2, ambientTrack3, ambientTrack4, guiTrack).forEach {
it.filters[0] = Gain(1f) it.filters[0] = PreGain(1f)
} }
guiTracks.forEach { guiTracks.forEach {
@@ -285,11 +285,11 @@ class AudioMixer : Disposable {
} }
convolveBusOpen.filters[1] = Convolv("basegame", "audio/convolution/EchoThief - PurgatoryChasm.bin", decibelsToFullscale(-6.0).toFloat()) convolveBusOpen.filters[1] = Convolv("basegame", "audio/convolution/EchoThief - PurgatoryChasm.bin", decibelsToFullscale(-6.0).toFloat())
convolveBusOpen.filters[2] = Gain(decibelsToFullscale(17.0).toFloat()) // don't make it too loud; it'll sound like a shit convolveBusOpen.filters[2] = PreGain(decibelsToFullscale(17.0).toFloat()) // don't make it too loud; it'll sound like a shit
convolveBusOpen.volume = 0.5 // will be controlled by the other updater which surveys the world convolveBusOpen.volume = 0.5 // will be controlled by the other updater which surveys the world
convolveBusCave.filters[1] = Convolv("basegame", "audio/convolution/EchoThief - WaterplacePark-trimmed.bin", decibelsToFullscale(-3.0).toFloat()) convolveBusCave.filters[1] = Convolv("basegame", "audio/convolution/EchoThief - WaterplacePark-trimmed.bin", decibelsToFullscale(-3.0).toFloat())
convolveBusCave.filters[2] = Gain(decibelsToFullscale(16.0).toFloat()) convolveBusCave.filters[2] = PreGain(decibelsToFullscale(16.0).toFloat())
convolveBusCave.volume = 0.5 // will be controlled by the other updater which surveys the world convolveBusCave.volume = 0.5 // will be controlled by the other updater which surveys the world
fadeBus.addSidechainInput(sumBus, 1.0 / 3.0) fadeBus.addSidechainInput(sumBus, 1.0 / 3.0)
@@ -392,12 +392,12 @@ class AudioMixer : Disposable {
// the real updates // the real updates
(Gdx.audio as? Lwjgl3Audio)?.update() (Gdx.audio as? Lwjgl3Audio)?.update()
masterTrack.volume = masterVolume masterTrack.volume = masterVolume
musicTrack.getFilter<Gain>().gain = musicVolume.toFloat() * 0.5f musicTrack.getFilter<PreGain>().gain = musicVolume.toFloat() * 0.5f
ambientTracks.forEach { ambientTracks.forEach {
it.getFilter<Gain>().gain = ambientVolume.toFloat() it.getFilter<PreGain>().gain = ambientVolume.toFloat()
} }
sfxSumBus.volume = sfxVolume sfxSumBus.volume = sfxVolume
guiTrack.getFilter<Gain>().gain = guiVolume.toFloat() guiTrack.getFilter<PreGain>().gain = guiVolume.toFloat()
// process fades // process fades

View File

@@ -9,7 +9,7 @@ import kotlin.math.roundToInt
/** /**
* Created by minjaesong on 2023-11-25. * Created by minjaesong on 2023-11-25.
*/ */
class Gain(var gain: Float): TerrarumAudioFilter() { class PreGain(var gain: Float): TerrarumAudioFilter() {
override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) { override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
for (i in 0 until App.audioBufferSize) { for (i in 0 until App.audioBufferSize) {
outbuf[0][i] = inbuf[0][i] * gain outbuf[0][i] = inbuf[0][i] * gain
@@ -25,7 +25,7 @@ class Gain(var gain: Float): TerrarumAudioFilter() {
override val debugViewHeight = 16 override val debugViewHeight = 16
override fun copyParamsFrom(other: TerrarumAudioFilter) { override fun copyParamsFrom(other: TerrarumAudioFilter) {
if (other is Gain) { if (other is PreGain) {
this.gain = other.gain this.gain = other.gain
} }
} }

View File

@@ -9,7 +9,7 @@ import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.audio.audiobank.MusicContainer import net.torvald.terrarum.audio.audiobank.MusicContainer
import net.torvald.terrarum.audio.dsp.Gain import net.torvald.terrarum.audio.dsp.PreGain
import net.torvald.terrarum.audio.dsp.NullFilter import net.torvald.terrarum.audio.dsp.NullFilter
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
@@ -361,7 +361,7 @@ class FixtureAlloyingFurnace : FixtureBase {
getTrackByAudio(static).let { getTrackByAudio(static).let {
if (it == null || (temperature > 0f && !it.isPlaying && !it.playRequested.get())) { if (it == null || (temperature > 0f && !it.isPlaying && !it.playRequested.get())) {
startAudio(static) { startAudio(static) {
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = PreGain(0f)
} }
} }
else if (it != null && it.isPlaying && temperature <= 0f) { else if (it != null && it.isPlaying && temperature <= 0f) {
@@ -371,10 +371,10 @@ class FixtureAlloyingFurnace : FixtureBase {
} }
if (it != null) { if (it != null) {
if (it.filters[filterIndex] !is Gain) // just in case... if (it.filters[filterIndex] !is PreGain) // just in case...
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = PreGain(0f)
(it.filters[filterIndex] as Gain).gain = (it.maxVolume * temperature * volRand.get()).toFloat() (it.filters[filterIndex] as PreGain).gain = (it.maxVolume * temperature * volRand.get()).toFloat()
} }
} }

View File

@@ -4,7 +4,7 @@ import net.torvald.gdx.graphics.Cvec
import net.torvald.spriteanimation.SheetSpriteAnimation import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.audio.audiobank.MusicContainer import net.torvald.terrarum.audio.audiobank.MusicContainer
import net.torvald.terrarum.audio.dsp.Gain import net.torvald.terrarum.audio.dsp.PreGain
import net.torvald.terrarum.audio.dsp.NullFilter import net.torvald.terrarum.audio.dsp.NullFilter
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
@@ -112,7 +112,7 @@ class FixtureFurnaceAndAnvil : FixtureBase, CraftingStation {
if (audioStatus == 0) { if (audioStatus == 0) {
startAudio(static) { startAudio(static) {
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = PreGain(0f)
audioStatus = 1 audioStatus = 1
} }
} }
@@ -124,10 +124,10 @@ class FixtureFurnaceAndAnvil : FixtureBase, CraftingStation {
if (it != null) { if (it != null) {
if (it.processor.streamBuf != null || it.playRequested.get()) { if (it.processor.streamBuf != null || it.playRequested.get()) {
if (it.filters[filterIndex] !is Gain) // just in case... if (it.filters[filterIndex] !is PreGain) // just in case...
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = PreGain(0f)
(it.filters[filterIndex] as Gain).gain = 0.4f * volRand.get() (it.filters[filterIndex] as PreGain).gain = 0.4f * volRand.get()
} }
else { else {
audioStatus = 0 audioStatus = 0

View File

@@ -9,7 +9,7 @@ import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.audio.audiobank.MusicContainer import net.torvald.terrarum.audio.audiobank.MusicContainer
import net.torvald.terrarum.audio.dsp.Gain import net.torvald.terrarum.audio.dsp.PreGain
import net.torvald.terrarum.audio.dsp.NullFilter import net.torvald.terrarum.audio.dsp.NullFilter
import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
@@ -348,7 +348,7 @@ class FixtureSmelterBasic : FixtureBase {
getTrackByAudio(static).let { getTrackByAudio(static).let {
if (it == null || (temperature > 0f && !it.isPlaying && !it.playRequested.get())) { if (it == null || (temperature > 0f && !it.isPlaying && !it.playRequested.get())) {
startAudio(static) { startAudio(static) {
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = PreGain(0f)
} }
} }
else if (it != null && it.isPlaying && temperature <= 0f) { else if (it != null && it.isPlaying && temperature <= 0f) {
@@ -358,10 +358,10 @@ class FixtureSmelterBasic : FixtureBase {
} }
if (it != null) { if (it != null) {
if (it.filters[filterIndex] !is Gain) // just in case... if (it.filters[filterIndex] !is PreGain) // just in case...
it.filters[filterIndex] = Gain(0f) it.filters[filterIndex] = PreGain(0f)
(it.filters[filterIndex] as Gain).gain = (it.maxVolume * temperature * volRand.get()).toFloat() (it.filters[filterIndex] as PreGain).gain = (it.maxVolume * temperature * volRand.get()).toFloat()
} }
} }

View File

@@ -58,6 +58,7 @@ abstract class TransactionListener {
catch (e: Throwable) { catch (e: Throwable) {
// if failed, notify the failure // if failed, notify the failure
System.err.println("Transaction failure: generic") System.err.println("Transaction failure: generic")
e.printStackTrace()
transaction.onFailure(e, state) transaction.onFailure(e, state)
} }
finally { finally {
@@ -67,7 +68,7 @@ abstract class TransactionListener {
} }
else { else {
System.err.println("Transaction failure: locked") System.err.println("Transaction failure: locked")
transaction.onFailure(LockedException(this, currentLock), state) transaction.onFailure(LockedException(transaction, this, currentLock), state)
} }
}.start() }.start()
} }
@@ -76,8 +77,8 @@ abstract class TransactionListener {
protected abstract fun commitTransaction(state: TransactionState) protected abstract fun commitTransaction(state: TransactionState)
} }
class LockedException(listener: TransactionListener, lockedBy: Transaction) : class LockedException(offendingTransaction: Transaction, listener: TransactionListener, lockedBy: Transaction) :
Exception("Transaction is rejected because the class '${listener.javaClass.canonicalName}' is locked by '${lockedBy.javaClass.canonicalName}'") Exception("Transaction '$offendingTransaction' is rejected because the class '$listener' is locked by '$lockedBy'")
@JvmInline value class TransactionState(val valueTable: HashMap<String, Any?>) { @JvmInline value class TransactionState(val valueTable: HashMap<String, Any?>) {
operator fun get(key: String) = valueTable[key] operator fun get(key: String) = valueTable[key]

View File

@@ -665,6 +665,7 @@ class BasicDebugInfoWindow : UICanvas() {
else it else it
}, },
//"C:${track.currentTrack?.codec ?: ""}", //"C:${track.currentTrack?.codec ?: ""}",
"GL:${if (track.doGaplessPlayback) "Y" else "N"}",
"R:${track.currentTrack?.samplingRate ?: ""}", "R:${track.currentTrack?.samplingRate ?: ""}",
).forEachIndexed { i, s -> ).forEachIndexed { i, s ->
// gauge background // gauge background
@@ -680,6 +681,14 @@ class BasicDebugInfoWindow : UICanvas() {
Toolkit.fillArea(batch, x.toFloat(), faderY - (i + 1) * 16f + 14f, STRIP_W * perc, 2f) Toolkit.fillArea(batch, x.toFloat(), faderY - (i + 1) * 16f + 14f, STRIP_W * perc, 2f)
} }
// fill the back if the track is in gapless mode
if (i == 1 && track.doGaplessPlayback) {
batch.color = COL_PROGRESS_GRAD2
Toolkit.fillArea(batch, x.toFloat(), faderY - (i + 1) * 16f, STRIP_W.toFloat(), 14f)
batch.color = COL_PROGRESS_GRAD
Toolkit.fillArea(batch, x.toFloat(), faderY - (i + 1) * 16f + 14f, STRIP_W.toFloat(), 2f)
}
batch.color = FILTER_NAME_ACTIVE batch.color = FILTER_NAME_ACTIVE
App.fontSmallNumbers.draw(batch, s, x + 3f, faderY - (i + 1) * 16f + 1f) App.fontSmallNumbers.draw(batch, s, x + 3f, faderY - (i + 1) * 16f + 1f)
} }