sound on memory wip

This commit is contained in:
minjaesong
2024-04-02 00:29:59 +09:00
parent a1a70274dd
commit 53f54a450d
6 changed files with 100 additions and 29 deletions

View File

@@ -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); }

View File

@@ -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()
}
}

View File

@@ -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)

View File

@@ -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()
}
}

View File

@@ -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) {

View File

@@ -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)
}