mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-12 19:44:05 +09:00
fix: RAM music should loop as they should now
This commit is contained in:
@@ -376,7 +376,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f)
|
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, AudioMixer.DEFAULT_FADEOUT_LEN / 3f)
|
||||||
App.audioMixer.musicTrack.nextTrack = null
|
App.audioMixer.musicTrack.nextTrack = null
|
||||||
ingame.musicGovernor.stopMusic(this)
|
ingame.musicGovernor.stopMusic(this)
|
||||||
thisMusic?.let { ingame.musicGovernor.queueMusicToPlayNext(it) }
|
if (thisMusic is MusicContainer) thisMusic.let { ingame.musicGovernor.queueMusicToPlayNext(it) }
|
||||||
iHitTheStopButton = true
|
iHitTheStopButton = true
|
||||||
}
|
}
|
||||||
else if (!shouldPlayerBeDisabled) {
|
else if (!shouldPlayerBeDisabled) {
|
||||||
@@ -472,7 +472,7 @@ class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
|
|||||||
iHitTheStopButton = false
|
iHitTheStopButton = false
|
||||||
stopRequested = false
|
stopRequested = false
|
||||||
}
|
}
|
||||||
resetPlaylistScroll(App.audioMixer.musicTrack.nextTrack)
|
resetPlaylistScroll(App.audioMixer.musicTrack.nextTrack as? MusicContainer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.github.strikerx3.jxinput.XInputDevice;
|
|||||||
import kotlin.jvm.functions.Function0;
|
import kotlin.jvm.functions.Function0;
|
||||||
import kotlin.text.Charsets;
|
import kotlin.text.Charsets;
|
||||||
import net.torvald.getcpuname.GetCpuName;
|
import net.torvald.getcpuname.GetCpuName;
|
||||||
|
import net.torvald.terrarum.audio.AudioBank;
|
||||||
import net.torvald.terrarum.audio.AudioMixer;
|
import net.torvald.terrarum.audio.AudioMixer;
|
||||||
import net.torvald.terrarum.audio.MusicContainer;
|
import net.torvald.terrarum.audio.MusicContainer;
|
||||||
import net.torvald.terrarum.audio.dsp.BinoPan;
|
import net.torvald.terrarum.audio.dsp.BinoPan;
|
||||||
@@ -554,11 +555,11 @@ public class App implements ApplicationListener {
|
|||||||
|
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("title_health1", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_take_a_break.tga")));
|
CommonResourcePool.INSTANCE.addToLoadingList("title_health1", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_take_a_break.tga")));
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("title_health2", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_distance.tga")));
|
CommonResourcePool.INSTANCE.addToLoadingList("title_health2", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_distance.tga")));
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bup", () -> new MusicContainer("haptic_bup", Gdx.files.internal("./assets/audio/effects/haptic_bup.ogg").file(), false, true, (MusicContainer m) -> { return null; }));
|
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bup", () -> new MusicContainer("haptic_bup", Gdx.files.internal("./assets/audio/effects/haptic_bup.ogg").file(), false, true, (AudioBank m) -> { return null; }));
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bap", () -> new MusicContainer("haptic_bap", Gdx.files.internal("./assets/audio/effects/haptic_bap.ogg").file(), false, true, (MusicContainer m) -> { return null; }));
|
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bap", () -> new MusicContainer("haptic_bap", Gdx.files.internal("./assets/audio/effects/haptic_bap.ogg").file(), false, true, (AudioBank m) -> { return null; }));
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bop", () -> new MusicContainer("haptic_bop", Gdx.files.internal("./assets/audio/effects/haptic_bop.ogg").file(), false, true, (MusicContainer m) -> { return null; }));
|
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bop", () -> new MusicContainer("haptic_bop", Gdx.files.internal("./assets/audio/effects/haptic_bop.ogg").file(), false, true, (AudioBank m) -> { return null; }));
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bep", () -> new MusicContainer("haptic_bep", Gdx.files.internal("./assets/audio/effects/haptic_bep.ogg").file(), false, true, (MusicContainer m) -> { return null; }));
|
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bep", () -> new MusicContainer("haptic_bep", Gdx.files.internal("./assets/audio/effects/haptic_bep.ogg").file(), false, true, (AudioBank m) -> { return null; }));
|
||||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bip", () -> new MusicContainer("haptic_bip", Gdx.files.internal("./assets/audio/effects/haptic_bip.ogg").file(), false, true, (MusicContainer m) -> { highPrioritySoundPlaying = false; return null; }));
|
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bip", () -> new MusicContainer("haptic_bip", Gdx.files.internal("./assets/audio/effects/haptic_bip.ogg").file(), false, true, (AudioBank m) -> { highPrioritySoundPlaying = false; return null; }));
|
||||||
// make loading list
|
// make loading list
|
||||||
CommonResourcePool.INSTANCE.loadAll();
|
CommonResourcePool.INSTANCE.loadAll();
|
||||||
|
|
||||||
|
|||||||
32
src/net/torvald/terrarum/audio/AudioBank.kt
Normal file
32
src/net/torvald/terrarum/audio/AudioBank.kt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package net.torvald.terrarum.audio
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Disposable
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2024-04-05.
|
||||||
|
*/
|
||||||
|
abstract class AudioBank : Disposable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromMusic(name: String, file: File, looping: Boolean = false, toRAM: Boolean = false, songFinishedHook: (AudioBank) -> Unit = {}): AudioBank {
|
||||||
|
return MusicContainer(name, file, looping, toRAM, songFinishedHook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val hash = System.nanoTime()
|
||||||
|
|
||||||
|
abstract fun makeCopy(): AudioBank
|
||||||
|
|
||||||
|
abstract val name: String
|
||||||
|
|
||||||
|
abstract val samplingRate: Int
|
||||||
|
abstract val channels: Int
|
||||||
|
abstract val totalSizeInSamples: Long
|
||||||
|
abstract fun currentPositionInSamples(): Long
|
||||||
|
|
||||||
|
abstract fun readBytes(buffer: ByteArray): Int
|
||||||
|
abstract fun reset()
|
||||||
|
|
||||||
|
abstract val songFinishedHook: (AudioBank) -> Unit
|
||||||
|
}
|
||||||
@@ -485,7 +485,7 @@ class AudioMixer : Disposable {
|
|||||||
|
|
||||||
private var ambientStopped = true
|
private var ambientStopped = true
|
||||||
|
|
||||||
fun startMusic(song: MusicContainer) {
|
fun startMusic(song: AudioBank) {
|
||||||
if (musicTrack.isPlaying) {
|
if (musicTrack.isPlaying) {
|
||||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||||
}
|
}
|
||||||
@@ -496,7 +496,7 @@ class AudioMixer : Disposable {
|
|||||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAmb(song: MusicContainer) {
|
fun startAmb(song: AudioBank) {
|
||||||
val ambientTrack = if (!ambientTrack1.streamPlaying.get())
|
val ambientTrack = if (!ambientTrack1.streamPlaying.get())
|
||||||
ambientTrack1
|
ambientTrack1
|
||||||
else if (!ambientTrack2.streamPlaying.get())
|
else if (!ambientTrack2.streamPlaying.get())
|
||||||
@@ -513,7 +513,7 @@ class AudioMixer : Disposable {
|
|||||||
// fade will be processed by the update()
|
// fade will be processed by the update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAmb1(song: MusicContainer) {
|
fun startAmb1(song: AudioBank) {
|
||||||
if (ambientTrack1.isPlaying == true) {
|
if (ambientTrack1.isPlaying == true) {
|
||||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||||
}
|
}
|
||||||
@@ -521,7 +521,7 @@ class AudioMixer : Disposable {
|
|||||||
// fade will be processed by the update()
|
// fade will be processed by the update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAmb2(song: MusicContainer) {
|
fun startAmb2(song: AudioBank) {
|
||||||
if (ambientTrack2.isPlaying == true) {
|
if (ambientTrack2.isPlaying == true) {
|
||||||
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
requestFadeOut(musicTrack, DEFAULT_FADEOUT_LEN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,12 +98,6 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
|
|||||||
|
|
||||||
bytesRead += read0(buffer, bytesRead)
|
bytesRead += read0(buffer, bytesRead)
|
||||||
}
|
}
|
||||||
// if isLooping=true, do gapless sampleRead but reads from itself
|
|
||||||
else if (track.currentTrack?.looping == true && bytesRead < buffer.size) {
|
|
||||||
track.currentTrack?.reset()
|
|
||||||
|
|
||||||
bytesRead += read0(buffer, bytesRead)
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRead
|
bytesRead
|
||||||
}, { purgeStreamBuf() }).also {
|
}, { purgeStreamBuf() }).also {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import net.torvald.terrarum.tryDispose
|
|||||||
|
|
||||||
class MusicCache(val trackName: String) : Disposable {
|
class MusicCache(val trackName: String) : Disposable {
|
||||||
|
|
||||||
private val cache = HashMap<String, MusicContainer>()
|
private val cache = HashMap<String, AudioBank>()
|
||||||
|
|
||||||
fun getOrPut(music: MusicContainer?): MusicContainer? {
|
fun getOrPut(music: AudioBank?): AudioBank? {
|
||||||
if (music != null)
|
if (music != null)
|
||||||
return cache.getOrPut(music.name) { music.makeCopy() }
|
return cache.getOrPut(music.name) { music.makeCopy() }
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -7,41 +7,51 @@ import com.badlogic.gdx.backends.lwjgl3.audio.Ogg
|
|||||||
import com.badlogic.gdx.backends.lwjgl3.audio.OggInputStream
|
import com.badlogic.gdx.backends.lwjgl3.audio.OggInputStream
|
||||||
import com.badlogic.gdx.backends.lwjgl3.audio.Wav
|
import com.badlogic.gdx.backends.lwjgl3.audio.Wav
|
||||||
import com.badlogic.gdx.files.FileHandle
|
import com.badlogic.gdx.files.FileHandle
|
||||||
import com.badlogic.gdx.utils.Disposable
|
|
||||||
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.reflection.forceInvoke
|
||||||
|
import net.torvald.terrarum.App.printdbg
|
||||||
import net.torvald.unsafe.UnsafeHelper
|
import net.torvald.unsafe.UnsafeHelper
|
||||||
import net.torvald.unsafe.UnsafePtr
|
import net.torvald.unsafe.UnsafePtr
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import javax.sound.sampled.AudioSystem
|
import javax.sound.sampled.AudioSystem
|
||||||
|
|
||||||
data class MusicContainer(
|
class MusicContainer(
|
||||||
val name: String,
|
override val name: String,
|
||||||
val file: File,
|
val file: File,
|
||||||
val looping: Boolean = false,
|
val looping: Boolean = false,
|
||||||
val toRAM: Boolean = false,
|
val toRAM: Boolean = false,
|
||||||
internal var songFinishedHook: (MusicContainer) -> Unit = {}
|
override var songFinishedHook: (AudioBank) -> Unit = {}
|
||||||
): Disposable {
|
): AudioBank() {
|
||||||
val samplingRate: Int
|
override val samplingRate: Int
|
||||||
|
override val channels: Int
|
||||||
val codec: String
|
val codec: String
|
||||||
|
|
||||||
var samplesReadCount = 0L; internal set
|
var samplesReadCount = 0L; internal set
|
||||||
var samplesTotal: Long
|
override val totalSizeInSamples: Long
|
||||||
|
private val totalSizeInBytes: Long
|
||||||
|
|
||||||
private val gdxMusic: Music = Gdx.audio.newMusic(FileHandle(file))
|
private val gdxMusic: Music = Gdx.audio.newMusic(FileHandle(file))
|
||||||
|
|
||||||
private var soundBuf: UnsafePtr? = null; private set
|
private var soundBuf: UnsafePtr? = null; private set
|
||||||
|
|
||||||
private val hash = System.nanoTime()
|
private val bytesPerSample: Int
|
||||||
|
|
||||||
init {
|
init {
|
||||||
gdxMusic.isLooping = looping
|
gdxMusic.isLooping = looping
|
||||||
|
|
||||||
// gdxMusic.setOnCompletionListener(songFinishedHook)
|
// gdxMusic.setOnCompletionListener(songFinishedHook)
|
||||||
|
|
||||||
|
channels = when (gdxMusic) {
|
||||||
|
is Wav.Music -> gdxMusic.extortField<Wav.WavInputStream>("input")!!.channels
|
||||||
|
is Ogg.Music -> gdxMusic.extortField<OggInputStream>("input")!!.channels
|
||||||
|
else -> 2
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesPerSample = 2 * channels
|
||||||
|
|
||||||
samplingRate = when (gdxMusic) {
|
samplingRate = when (gdxMusic) {
|
||||||
is Wav.Music -> {
|
is Wav.Music -> {
|
||||||
val rate = gdxMusic.extortField<Wav.WavInputStream>("input")!!.sampleRate
|
val rate = gdxMusic.extortField<Wav.WavInputStream>("input")!!.sampleRate
|
||||||
@@ -80,55 +90,147 @@ data class MusicContainer(
|
|||||||
if (it.last() == "Music") it.dropLast(1).last() else it.last()
|
if (it.last() == "Music") it.dropLast(1).last() else it.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesTotal = when (gdxMusic) {
|
totalSizeInSamples = when (gdxMusic) {
|
||||||
is Wav.Music -> getWavFileSampleCount(file)
|
is Wav.Music -> getWavFileSampleCount(file)
|
||||||
is Ogg.Music -> getOggFileSampleCount(file)
|
is Ogg.Music -> getOggFileSampleCount(file)
|
||||||
is Mp3.Music -> getMp3FileSampleCount(file)
|
is Mp3.Music -> getMp3FileSampleCount(file)
|
||||||
else -> Long.MAX_VALUE
|
else -> Long.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
totalSizeInBytes = totalSizeInSamples * 2 * channels
|
||||||
|
|
||||||
|
|
||||||
if (toRAM) {
|
if (toRAM) {
|
||||||
if (samplesTotal == Long.MAX_VALUE) throw IllegalStateException("Could not read sample count")
|
if (totalSizeInSamples == Long.MAX_VALUE) throw IllegalStateException("Could not read sample count")
|
||||||
|
|
||||||
val readSize = 8192
|
val readSize = 8192
|
||||||
var readCount = 0L
|
var readCount = 0L
|
||||||
val readBuf = ByteArray(readSize)
|
val readBuf = ByteArray(readSize)
|
||||||
|
|
||||||
soundBuf = UnsafeHelper.allocate(4L * samplesTotal)
|
soundBuf = UnsafeHelper.allocate(totalSizeInBytes)
|
||||||
|
|
||||||
while (readCount < samplesTotal) {
|
while (readCount < totalSizeInBytes) {
|
||||||
val read = gdxMusic.forceInvoke<Int>("read", arrayOf(readBuf))!!.toLong()
|
gdxMusic.forceInvoke<Int>("read", arrayOf(readBuf))!!.toLong() // its return value will be useless for looping=true
|
||||||
|
val read = minOf(readSize.toLong(), (totalSizeInBytes - readCount))
|
||||||
|
|
||||||
UnsafeHelper.memcpyRaw(readBuf, UnsafeHelper.getArrayOffset(readBuf), null, soundBuf!!.ptr + readCount, read)
|
UnsafeHelper.memcpyRaw(readBuf, UnsafeHelper.getArrayOffset(readBuf), null, soundBuf!!.ptr + readCount, read)
|
||||||
|
|
||||||
readCount += read
|
readCount += read
|
||||||
|
|
||||||
|
// printdbg(this, "read $readCount/$totalSizeInBytes bytes (${readCount/(2*channels)}/$totalSizeInSamples samples)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readBytes(buffer: ByteArray): Int {
|
private fun read0(buffer: ByteArray, bytesRead: Int): Int {
|
||||||
|
val tmpBuf = ByteArray(buffer.size - bytesRead)
|
||||||
|
val newRead = readBytes(tmpBuf)
|
||||||
|
|
||||||
|
System.arraycopy(tmpBuf, 0, buffer, bytesRead, tmpBuf.size)
|
||||||
|
|
||||||
|
return newRead
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readBytes(buffer: ByteArray): Int {
|
||||||
if (soundBuf == null) {
|
if (soundBuf == null) {
|
||||||
val bytesRead = gdxMusic.forceInvoke<Int>("read", arrayOf(buffer)) ?: 0
|
val bytesRead = gdxMusic.forceInvoke<Int>("read", arrayOf(buffer)) ?: 0
|
||||||
samplesReadCount += bytesRead / 4
|
samplesReadCount += bytesRead / bytesPerSample
|
||||||
|
|
||||||
|
if (looping && bytesRead < buffer.size) {
|
||||||
|
reset()
|
||||||
|
|
||||||
|
val remainder = buffer.size - bytesRead
|
||||||
|
|
||||||
|
val fullCopyCounts = remainder / totalSizeInBytes
|
||||||
|
val partialCopyCountsInBytes = (remainder % totalSizeInBytes).toInt()
|
||||||
|
|
||||||
|
var start = UnsafeHelper.getArrayOffset(buffer).toInt() + bytesRead
|
||||||
|
|
||||||
|
val fullbuf = ByteArray(totalSizeInBytes.toInt())
|
||||||
|
// make full block copies
|
||||||
|
for (i in 0 until fullCopyCounts) {
|
||||||
|
gdxMusic.forceInvoke<Int>("read", arrayOf(fullbuf))
|
||||||
|
reset()
|
||||||
|
|
||||||
|
System.arraycopy(fullbuf, 0, buffer, start, fullbuf.size)
|
||||||
|
|
||||||
|
start += totalSizeInBytes.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the remainders from the start of the samples
|
||||||
|
val partialBuf = ByteArray(partialCopyCountsInBytes)
|
||||||
|
gdxMusic.forceInvoke<Int>("read", arrayOf(partialBuf))
|
||||||
|
System.arraycopy(partialBuf, 0, buffer, start, partialCopyCountsInBytes)
|
||||||
|
|
||||||
|
samplesReadCount += partialCopyCountsInBytes / bytesPerSample
|
||||||
|
}
|
||||||
|
|
||||||
return bytesRead
|
return bytesRead
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val bytesToRead = minOf(buffer.size.toLong(), 4 * (samplesTotal - samplesReadCount))
|
val bytesToRead = minOf(buffer.size.toLong(), 2 * channels * (totalSizeInSamples - samplesReadCount))
|
||||||
if (bytesToRead <= 0) return bytesToRead.toInt()
|
|
||||||
|
|
||||||
UnsafeHelper.memcpyRaw(null, soundBuf!!.ptr + samplesReadCount * 4, buffer, UnsafeHelper.getArrayOffset(buffer), bytesToRead)
|
if (!looping && bytesToRead <= 0) return bytesToRead.toInt()
|
||||||
|
// if (looping) printdbg(this, "toRAM music loop (bytes cursor: $samplesReadCount/$totalSizeInSamples, bytesToRead=$bytesToRead, buffer.size=${buffer.size})")
|
||||||
|
|
||||||
|
UnsafeHelper.memcpyRaw(
|
||||||
|
null,
|
||||||
|
soundBuf!!.ptr + samplesReadCount * bytesPerSample,
|
||||||
|
buffer,
|
||||||
|
UnsafeHelper.getArrayOffset(buffer),
|
||||||
|
bytesToRead
|
||||||
|
)
|
||||||
|
|
||||||
|
samplesReadCount += bytesToRead / bytesPerSample
|
||||||
|
|
||||||
|
|
||||||
|
// reached the end of the "tape"
|
||||||
|
if (looping && bytesToRead < buffer.size) {
|
||||||
|
|
||||||
|
val remainder = buffer.size - bytesToRead
|
||||||
|
|
||||||
|
val fullCopyCounts = remainder / totalSizeInBytes
|
||||||
|
val partialCopyCountsInBytes = remainder % totalSizeInBytes
|
||||||
|
|
||||||
|
var start = UnsafeHelper.getArrayOffset(buffer) + bytesToRead
|
||||||
|
|
||||||
|
// make full block copies
|
||||||
|
for (i in 0 until fullCopyCounts) {
|
||||||
|
UnsafeHelper.memcpyRaw(
|
||||||
|
null,
|
||||||
|
soundBuf!!.ptr,
|
||||||
|
buffer,
|
||||||
|
start,
|
||||||
|
totalSizeInBytes
|
||||||
|
)
|
||||||
|
|
||||||
|
start += totalSizeInBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the remainders from the start of the "tape"
|
||||||
|
UnsafeHelper.memcpyRaw(
|
||||||
|
null,
|
||||||
|
soundBuf!!.ptr,
|
||||||
|
buffer,
|
||||||
|
start,
|
||||||
|
partialCopyCountsInBytes
|
||||||
|
)
|
||||||
|
|
||||||
|
samplesReadCount = partialCopyCountsInBytes / bytesPerSample
|
||||||
|
}
|
||||||
|
|
||||||
|
if (looping) return buffer.size
|
||||||
|
|
||||||
samplesReadCount += bytesToRead / 4
|
|
||||||
return bytesToRead.toInt()
|
return bytesToRead.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
override fun reset() {
|
||||||
samplesReadCount = 0L
|
samplesReadCount = 0L
|
||||||
gdxMusic.forceInvoke<Int>("reset", arrayOf())
|
gdxMusic.forceInvoke<Int>("reset", arrayOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun currentPositionInSamples() = samplesReadCount
|
||||||
|
|
||||||
private fun getWavFileSampleCount(file: File): Long {
|
private fun getWavFileSampleCount(file: File): Long {
|
||||||
return try {
|
return try {
|
||||||
val ais = AudioSystem.getAudioInputStream(file)
|
val ais = AudioSystem.getAudioInputStream(file)
|
||||||
@@ -186,7 +288,7 @@ data class MusicContainer(
|
|||||||
soundBuf?.destroy()
|
soundBuf?.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeCopy(): MusicContainer {
|
override fun makeCopy(): AudioBank {
|
||||||
val new = MusicContainer(name, file, looping, false, songFinishedHook)
|
val new = MusicContainer(name, file, looping, false, songFinishedHook)
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class TerrarumAudioMixerTrack(
|
|||||||
acc or (c shl (5*i))
|
acc or (c shl (5*i))
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentTrack: MusicContainer? = null
|
var currentTrack: AudioBank? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = if (value == null)
|
field = if (value == null)
|
||||||
null
|
null
|
||||||
@@ -50,7 +50,7 @@ class TerrarumAudioMixerTrack(
|
|||||||
musicCache.getOrPut(value)
|
musicCache.getOrPut(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextTrack: MusicContainer? = null
|
var nextTrack: AudioBank? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = if (value == null)
|
field = if (value == null)
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import net.torvald.terrarum.App.printdbg
|
|||||||
import net.torvald.terrarum.App.printdbgerr
|
import net.torvald.terrarum.App.printdbgerr
|
||||||
import net.torvald.terrarum.INGAME
|
import net.torvald.terrarum.INGAME
|
||||||
import net.torvald.terrarum.Terrarum
|
import net.torvald.terrarum.Terrarum
|
||||||
|
import net.torvald.terrarum.audio.AudioBank
|
||||||
import net.torvald.terrarum.audio.MusicContainer
|
import net.torvald.terrarum.audio.MusicContainer
|
||||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
|
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
|
||||||
import net.torvald.terrarum.audio.TrackVolume
|
import net.torvald.terrarum.audio.TrackVolume
|
||||||
@@ -216,7 +217,7 @@ abstract class Actor : Comparable<Actor>, Runnable {
|
|||||||
|
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
open @Event fun onAudioInterrupt(music: MusicContainer) {
|
open @Event fun onAudioInterrupt(music: AudioBank) {
|
||||||
music.songFinishedHook(music)
|
music.songFinishedHook(music)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.utils.GdxRuntimeException
|
|||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
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.AudioBank
|
||||||
import net.torvald.terrarum.audio.AudioMixer
|
import net.torvald.terrarum.audio.AudioMixer
|
||||||
import net.torvald.terrarum.audio.MusicContainer
|
import net.torvald.terrarum.audio.MusicContainer
|
||||||
import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH
|
import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH
|
||||||
@@ -199,17 +200,15 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
var playCaller: Any? = null; private set
|
var playCaller: Any? = null; private set
|
||||||
var stopCallTime: Long? = null; private set
|
var stopCallTime: Long? = null; private set
|
||||||
|
|
||||||
private fun stopMusic0(song: MusicContainer?, callStopMusicHook: Boolean = true, customPauseLen: Float? = null) {
|
private fun stopMusic0(song: AudioBank?, callStopMusicHook: Boolean = true, customPauseLen: Float? = null) {
|
||||||
musicState = if (customPauseLen == Float.POSITIVE_INFINITY) STATE_INIT else STATE_INTERMISSION
|
musicState = if (customPauseLen == Float.POSITIVE_INFINITY) STATE_INIT else STATE_INTERMISSION
|
||||||
// printdbg(this, "stopMusic1 customLen=$customPauseLen, stateNow: $musicState, called by")
|
// printdbg(this, "stopMusic1 customLen=$customPauseLen, stateNow: $musicState, called by")
|
||||||
// printStackTrace(this)
|
// printStackTrace(this)
|
||||||
intermissionAkku = 0f
|
intermissionAkku = 0f
|
||||||
intermissionLength = customPauseLen ?: getRandomMusicInterval()
|
intermissionLength = customPauseLen ?: getRandomMusicInterval()
|
||||||
musicFired = false
|
musicFired = false
|
||||||
if (callStopMusicHook && musicStopHooks.isNotEmpty()) musicStopHooks.forEach {
|
if (callStopMusicHook && musicStopHooks.isNotEmpty() && song is MusicContainer) musicStopHooks.forEach {
|
||||||
if (song != null) {
|
it(song)
|
||||||
it(song)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// printdbg(this, "StopMusic Intermission: $intermissionLength seconds")
|
// printdbg(this, "StopMusic Intermission: $intermissionLength seconds")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
if (it.length > 7) it.replace(" ", "").let { it.substring(0 until minOf(it.length, 7)) }
|
if (it.length > 7) it.replace(" ", "").let { it.substring(0 until minOf(it.length, 7)) }
|
||||||
else it
|
else it
|
||||||
},
|
},
|
||||||
"C:${track.currentTrack?.codec ?: ""}",
|
//"C:${track.currentTrack?.codec ?: ""}",
|
||||||
"R:${track.currentTrack?.samplingRate ?: ""}",
|
"R:${track.currentTrack?.samplingRate ?: ""}",
|
||||||
).forEachIndexed { i, s ->
|
).forEachIndexed { i, s ->
|
||||||
// gauge background
|
// gauge background
|
||||||
@@ -673,7 +673,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
|||||||
|
|
||||||
// fill the song title line with a progress bar
|
// fill the song title line with a progress bar
|
||||||
if (i == 0 && track.currentTrack != null) {
|
if (i == 0 && track.currentTrack != null) {
|
||||||
val perc = (track.currentTrack!!.samplesReadCount.toFloat() / track.currentTrack!!.samplesTotal).coerceAtMost(1f)
|
val perc = (track.currentTrack!!.currentPositionInSamples().toFloat() / track.currentTrack!!.totalSizeInSamples).coerceAtMost(1f)
|
||||||
batch.color = COL_PROGRESS_GRAD2
|
batch.color = COL_PROGRESS_GRAD2
|
||||||
Toolkit.fillArea(batch, x.toFloat(), faderY - (i + 1) * 16f, STRIP_W * perc, 14f)
|
Toolkit.fillArea(batch, x.toFloat(), faderY - (i + 1) * 16f, STRIP_W * perc, 14f)
|
||||||
batch.color = COL_PROGRESS_GRAD
|
batch.color = COL_PROGRESS_GRAD
|
||||||
|
|||||||
Reference in New Issue
Block a user