mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
sound on memory wip
This commit is contained in:
@@ -554,9 +554,9 @@ 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_health2", () -> new Texture(Gdx.files.internal("./assets/graphics/gui/health_distance.tga")));
|
||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bop", () -> new MusicContainer("haptic_bop", Gdx.files.internal("./assets/audio/effects/haptic_bop.ogg").file(), false, (Music m) -> { return null; }));
|
||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bup", () -> new MusicContainer("haptic_bup", Gdx.files.internal("./assets/audio/effects/haptic_bup.ogg").file(), false, (Music m) -> { return null; }));
|
||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bip", () -> new MusicContainer("haptic_bip", Gdx.files.internal("./assets/audio/effects/haptic_bip.ogg").file(), false, (Music m) -> { highPrioritySoundPlaying = false; return null; }));
|
||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bop", () -> new MusicContainer(true, "haptic_bop", Gdx.files.internal("./assets/audio/effects/haptic_bop.ogg").file(), false, (Music m) -> { return null; }));
|
||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bup", () -> new MusicContainer(true, "haptic_bup", Gdx.files.internal("./assets/audio/effects/haptic_bup.ogg").file(), false, (Music m) -> { return null; }));
|
||||
CommonResourcePool.INSTANCE.addToLoadingList("sound:haptic_bip", () -> new MusicContainer(true, "haptic_bip", Gdx.files.internal("./assets/audio/effects/haptic_bip.ogg").file(), false, (Music m) -> { highPrioritySoundPlaying = false; return null; }));
|
||||
// make loading list
|
||||
CommonResourcePool.INSTANCE.loadAll();
|
||||
|
||||
@@ -1971,13 +1971,15 @@ public class App implements ApplicationListener {
|
||||
|
||||
public static void playGUIsound(MusicContainer sound, double volume, float pan) {
|
||||
if (!highPrioritySoundPlaying) {
|
||||
var it = audioMixer.getGuiTrack();
|
||||
it.stop();
|
||||
it.setCurrentTrack(sound);
|
||||
it.setMaxVolumeFun(() -> volume);
|
||||
it.setVolume(volume);
|
||||
((BinoPan) it.getFilters()[1]).setPan(pan);
|
||||
it.play();
|
||||
var it = audioMixer.getFreeGuiTrackNoMatterWhat();
|
||||
if (it != null) {
|
||||
it.stop();
|
||||
it.setCurrentTrack(sound);
|
||||
it.setMaxVolumeFun(() -> volume);
|
||||
it.setVolume(volume);
|
||||
((BinoPan) it.getFilters()[0]).setPan(pan);
|
||||
it.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void playGUIsound(MusicContainer sound, double volume) { playGUIsound(sound, volume, 0.0f); }
|
||||
@@ -1985,13 +1987,13 @@ public class App implements ApplicationListener {
|
||||
|
||||
public static void playGUIsoundHigh(MusicContainer sound, double volume, float pan) {
|
||||
// TODO when a sound is played thru this function, other sound play calls thru playGUIsound are ignored until this sound finishes playing
|
||||
var it = audioMixer.getGuiTrack();
|
||||
var it = audioMixer.getFreeGuiTrackNoMatterWhat();
|
||||
highPrioritySoundPlaying = true;
|
||||
it.stop();
|
||||
it.setCurrentTrack(sound);
|
||||
it.setMaxVolumeFun(() -> volume);
|
||||
it.setVolume(volume);
|
||||
((BinoPan) it.getFilters()[1]).setPan(pan);
|
||||
((BinoPan) it.getFilters()[0]).setPan(pan);
|
||||
it.play();
|
||||
}
|
||||
public static void playGUIsoundHigh(MusicContainer sound, double volume) { playGUIsoundHigh(sound, volume, 0.0f); }
|
||||
|
||||
@@ -66,7 +66,7 @@ class AudioMixer : Disposable {
|
||||
else if (it == 9) "\u00D9Open\u00D9" // convolution1
|
||||
else if (it == 10) "\u00D9Cave\u00D9" // convolution2
|
||||
else if (it == 11) "\u00F0 \u00DA \u00F0" // fade
|
||||
else "Trk${it+1}", trackType = if (it >= 7 || it == 3) TrackType.BUS else TrackType.STATIC_SOURCE, maxVolumeFun = {
|
||||
else "Trk${it+1}", trackType = if (it >= 6 || it == 3) TrackType.BUS else TrackType.STATIC_SOURCE, maxVolumeFun = {
|
||||
when (it) {
|
||||
0 -> { musicVolume }
|
||||
4 -> { ambientVolume }
|
||||
@@ -115,10 +115,29 @@ class AudioMixer : Disposable {
|
||||
ambientTrack1, ambientTrack2, ambientTrack3, ambientTrack4
|
||||
)
|
||||
|
||||
val guiTracks = Array(4) { TerrarumAudioMixerTrack("GUI${it+1}", TrackType.STATIC_SOURCE) }
|
||||
|
||||
var processing = false
|
||||
|
||||
var actorNowPlaying = Terrarum.ingame?.actorNowPlaying; private set
|
||||
|
||||
fun getFreeGuiTrackNoMatterWhat(): TerrarumAudioMixerTrack {
|
||||
synchronized(this) {
|
||||
val it = getFreeGuiTrack() ?: guiTracks.minBy { it.playStartedTime }.also { it.checkedOutTime = System.nanoTime() }
|
||||
println("GuiTrack ${it.name}")
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
fun getFreeGuiTrack(): TerrarumAudioMixerTrack? {
|
||||
synchronized(this) {
|
||||
return guiTracks.minByOrNull { maxOf(it.checkedOutTime, it.playStartedTime) }
|
||||
.also {
|
||||
it?.checkedOutTime = System.nanoTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return oldest dynamic track, even if the track is currently playing
|
||||
*/
|
||||
@@ -169,6 +188,12 @@ class AudioMixer : Disposable {
|
||||
catch (e: Throwable) { e.printStackTrace() }
|
||||
}
|
||||
}
|
||||
guiTracks.forEach {
|
||||
if (!it.processor.paused) {
|
||||
try { it.processor.run() }
|
||||
catch (e: Throwable) { e.printStackTrace() }
|
||||
}
|
||||
}
|
||||
tracks.forEach {
|
||||
if (!it.processor.paused) {
|
||||
try { it.processor.run() }
|
||||
@@ -230,7 +255,10 @@ class AudioMixer : Disposable {
|
||||
it.filters[0] = Gain(1f)
|
||||
}
|
||||
|
||||
guiTrack.filters[1] = BinoPan(0f)
|
||||
guiTracks.forEach {
|
||||
guiTrack.addSidechainInput(it, 1.0)
|
||||
it.filters[0] = BinoPan(0f)
|
||||
}
|
||||
|
||||
masterTrack.filters[0] = SoftClp
|
||||
masterTrack.filters[1] = Buffer
|
||||
@@ -285,6 +313,7 @@ class AudioMixer : Disposable {
|
||||
/*parallelProcessingSchedule =
|
||||
arrayOf(musicTrack, ambientTrack, guiTrack).sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
||||
dynamicTracks.sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
||||
guiTracks +
|
||||
arrayOf(sfxSumBus, sumBus, convolveBusOpen, convolveBusCave).sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
||||
arrayOf(fadeBus) +
|
||||
arrayOf(masterTrack)*/
|
||||
@@ -552,6 +581,7 @@ class AudioMixer : Disposable {
|
||||
fun reset() {
|
||||
ambientStopped = true
|
||||
dynamicTracks.forEach { it.stop() }
|
||||
guiTracks.forEach { it.stop() }
|
||||
tracks.filter { it.trackType == TrackType.STATIC_SOURCE }.forEach { it.stop() }
|
||||
tracks.forEach {
|
||||
it.processor.purgeBuffer()
|
||||
@@ -574,6 +604,7 @@ class AudioMixer : Disposable {
|
||||
// feedingThread.join()
|
||||
tracks.forEach { it.tryDispose() }
|
||||
dynamicTracks.forEach { it.tryDispose() }
|
||||
guiTracks.forEach { it.tryDispose() }
|
||||
masterTrack.tryDispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.torvald.terrarum.audio
|
||||
|
||||
import com.badlogic.gdx.utils.Queue
|
||||
import net.torvald.reflection.forceInvoke
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.audio.AudioMixer.Companion.DS_FLTIDX_LOW
|
||||
@@ -102,7 +101,7 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
|
||||
bytesRead += read0(buffer, bytesRead)
|
||||
}
|
||||
// if isLooping=true, do gapless sampleRead but reads from itself
|
||||
else if (track.currentTrack?.gdxMusic?.isLooping == true && bytesRead < buffer.size) {
|
||||
else if (track.currentTrack?.looping == true && bytesRead < buffer.size) {
|
||||
track.currentTrack?.reset()
|
||||
|
||||
bytesRead += read0(buffer, bytesRead)
|
||||
|
||||
@@ -12,28 +12,32 @@ import com.jcraft.jorbis.VorbisFile
|
||||
import javazoom.jl.decoder.Bitstream
|
||||
import net.torvald.reflection.extortField
|
||||
import net.torvald.reflection.forceInvoke
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.tryDispose
|
||||
import net.torvald.unsafe.UnsafeHelper
|
||||
import net.torvald.unsafe.UnsafePtr
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import javax.sound.sampled.AudioSystem
|
||||
|
||||
data class MusicContainer(
|
||||
val toRAM: Boolean = false,
|
||||
val name: String,
|
||||
val file: File,
|
||||
val loop: Boolean = false,
|
||||
val looping: Boolean = false,
|
||||
internal var songFinishedHook: (Music) -> Unit = {}
|
||||
): Disposable {
|
||||
val samplingRate: Int
|
||||
val codec: String
|
||||
|
||||
var samplesRead = 0L; internal set
|
||||
var samplesReadCount = 0L; internal set
|
||||
val samplesTotal: Long
|
||||
|
||||
val gdxMusic = Gdx.audio.newMusic(FileHandle(file))
|
||||
private val gdxMusic: Music = Gdx.audio.newMusic(FileHandle(file))
|
||||
|
||||
private var soundBuf: UnsafePtr? = null; private set
|
||||
|
||||
|
||||
init {
|
||||
gdxMusic.isLooping = loop
|
||||
gdxMusic.isLooping = looping
|
||||
|
||||
gdxMusic.setOnCompletionListener(songFinishedHook)
|
||||
|
||||
@@ -82,6 +86,45 @@ data class MusicContainer(
|
||||
else -> Long.MAX_VALUE
|
||||
}
|
||||
|
||||
|
||||
if (toRAM) {
|
||||
if (samplesTotal == Long.MAX_VALUE) throw IllegalStateException("Could not read sample count")
|
||||
|
||||
val readSize = 8192
|
||||
var readCount = 0L
|
||||
val readBuf = ByteArray(readSize)
|
||||
|
||||
soundBuf = UnsafeHelper.allocate(4L * samplesTotal)
|
||||
|
||||
while (readCount < samplesTotal) {
|
||||
val read = gdxMusic.forceInvoke<Int>("read", arrayOf(readBuf))!!.toLong()
|
||||
|
||||
UnsafeHelper.memcpyRaw(readBuf, UnsafeHelper.getArrayOffset(readBuf), null, soundBuf!!.ptr + readCount, read)
|
||||
|
||||
readCount += read
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun readBytes(buffer: ByteArray): Int {
|
||||
if (soundBuf == null) {
|
||||
val bytesRead = gdxMusic.forceInvoke<Int>("read", arrayOf(buffer)) ?: 0
|
||||
samplesReadCount += bytesRead / 4
|
||||
return bytesRead
|
||||
}
|
||||
else {
|
||||
val bytesToRead = minOf(buffer.size.toLong(), 4 * (samplesTotal - samplesReadCount))
|
||||
|
||||
UnsafeHelper.memcpyRaw(null, soundBuf!!.ptr, buffer, UnsafeHelper.getArrayOffset(buffer), bytesToRead)
|
||||
|
||||
samplesReadCount += bytesToRead / 4
|
||||
return bytesToRead.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
samplesReadCount = 0L
|
||||
gdxMusic.forceInvoke<Int>("reset", arrayOf())
|
||||
}
|
||||
|
||||
private fun getWavFileSampleCount(file: File): Long {
|
||||
@@ -133,14 +176,10 @@ data class MusicContainer(
|
||||
|
||||
override fun toString() = if (name.isEmpty()) file.nameWithoutExtension else name
|
||||
|
||||
fun reset() {
|
||||
samplesRead = 0L
|
||||
gdxMusic.forceInvoke<Int>("reset", arrayOf())
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = this.file.path == (other as MusicContainer).file.path
|
||||
|
||||
override fun dispose() {
|
||||
gdxMusic.dispose()
|
||||
soundBuf?.destroy()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.torvald.terrarum.modulebasegame
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.*
|
||||
@@ -148,7 +147,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
||||
MusicContainer(
|
||||
fileHandle.nameWithoutExtension().replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
||||
fileHandle.file(),
|
||||
loop = true,
|
||||
looping = true,
|
||||
)
|
||||
}
|
||||
catch (e: GdxRuntimeException) {
|
||||
|
||||
@@ -284,6 +284,7 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
}
|
||||
|
||||
object UIItemAccessibilityUtil {
|
||||
// TODO have multiple bop instances (num of copies equal to guiTracks), then play the track with its index according to getFreeGuiTrack()
|
||||
fun playHapticCursorHovered() {
|
||||
App.playGUIsound(CommonResourcePool.getAs("sound:haptic_bup"), 0.1666666)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user