mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-10 18:44:05 +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_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_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_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("haptic_bup", Gdx.files.internal("./assets/audio/effects/haptic_bup.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("haptic_bip", Gdx.files.internal("./assets/audio/effects/haptic_bip.ogg").file(), false, (Music m) -> { highPrioritySoundPlaying = false; 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
|
// make loading list
|
||||||
CommonResourcePool.INSTANCE.loadAll();
|
CommonResourcePool.INSTANCE.loadAll();
|
||||||
|
|
||||||
@@ -1971,13 +1971,15 @@ public class App implements ApplicationListener {
|
|||||||
|
|
||||||
public static void playGUIsound(MusicContainer sound, double volume, float pan) {
|
public static void playGUIsound(MusicContainer sound, double volume, float pan) {
|
||||||
if (!highPrioritySoundPlaying) {
|
if (!highPrioritySoundPlaying) {
|
||||||
var it = audioMixer.getGuiTrack();
|
var it = audioMixer.getFreeGuiTrackNoMatterWhat();
|
||||||
it.stop();
|
if (it != null) {
|
||||||
it.setCurrentTrack(sound);
|
it.stop();
|
||||||
it.setMaxVolumeFun(() -> volume);
|
it.setCurrentTrack(sound);
|
||||||
it.setVolume(volume);
|
it.setMaxVolumeFun(() -> volume);
|
||||||
((BinoPan) it.getFilters()[1]).setPan(pan);
|
it.setVolume(volume);
|
||||||
it.play();
|
((BinoPan) it.getFilters()[0]).setPan(pan);
|
||||||
|
it.play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static void playGUIsound(MusicContainer sound, double volume) { playGUIsound(sound, volume, 0.0f); }
|
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) {
|
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
|
// 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;
|
highPrioritySoundPlaying = true;
|
||||||
it.stop();
|
it.stop();
|
||||||
it.setCurrentTrack(sound);
|
it.setCurrentTrack(sound);
|
||||||
it.setMaxVolumeFun(() -> volume);
|
it.setMaxVolumeFun(() -> volume);
|
||||||
it.setVolume(volume);
|
it.setVolume(volume);
|
||||||
((BinoPan) it.getFilters()[1]).setPan(pan);
|
((BinoPan) it.getFilters()[0]).setPan(pan);
|
||||||
it.play();
|
it.play();
|
||||||
}
|
}
|
||||||
public static void playGUIsoundHigh(MusicContainer sound, double volume) { playGUIsoundHigh(sound, volume, 0.0f); }
|
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 == 9) "\u00D9Open\u00D9" // convolution1
|
||||||
else if (it == 10) "\u00D9Cave\u00D9" // convolution2
|
else if (it == 10) "\u00D9Cave\u00D9" // convolution2
|
||||||
else if (it == 11) "\u00F0 \u00DA \u00F0" // fade
|
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) {
|
when (it) {
|
||||||
0 -> { musicVolume }
|
0 -> { musicVolume }
|
||||||
4 -> { ambientVolume }
|
4 -> { ambientVolume }
|
||||||
@@ -115,10 +115,29 @@ class AudioMixer : Disposable {
|
|||||||
ambientTrack1, ambientTrack2, ambientTrack3, ambientTrack4
|
ambientTrack1, ambientTrack2, ambientTrack3, ambientTrack4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val guiTracks = Array(4) { TerrarumAudioMixerTrack("GUI${it+1}", TrackType.STATIC_SOURCE) }
|
||||||
|
|
||||||
var processing = false
|
var processing = false
|
||||||
|
|
||||||
var actorNowPlaying = Terrarum.ingame?.actorNowPlaying; private set
|
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
|
* Return oldest dynamic track, even if the track is currently playing
|
||||||
*/
|
*/
|
||||||
@@ -169,6 +188,12 @@ class AudioMixer : Disposable {
|
|||||||
catch (e: Throwable) { e.printStackTrace() }
|
catch (e: Throwable) { e.printStackTrace() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
guiTracks.forEach {
|
||||||
|
if (!it.processor.paused) {
|
||||||
|
try { it.processor.run() }
|
||||||
|
catch (e: Throwable) { e.printStackTrace() }
|
||||||
|
}
|
||||||
|
}
|
||||||
tracks.forEach {
|
tracks.forEach {
|
||||||
if (!it.processor.paused) {
|
if (!it.processor.paused) {
|
||||||
try { it.processor.run() }
|
try { it.processor.run() }
|
||||||
@@ -230,7 +255,10 @@ class AudioMixer : Disposable {
|
|||||||
it.filters[0] = Gain(1f)
|
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[0] = SoftClp
|
||||||
masterTrack.filters[1] = Buffer
|
masterTrack.filters[1] = Buffer
|
||||||
@@ -285,6 +313,7 @@ class AudioMixer : Disposable {
|
|||||||
/*parallelProcessingSchedule =
|
/*parallelProcessingSchedule =
|
||||||
arrayOf(musicTrack, ambientTrack, guiTrack).sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
arrayOf(musicTrack, ambientTrack, guiTrack).sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
||||||
dynamicTracks.sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
dynamicTracks.sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
||||||
|
guiTracks +
|
||||||
arrayOf(sfxSumBus, sumBus, convolveBusOpen, convolveBusCave).sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
arrayOf(sfxSumBus, sumBus, convolveBusOpen, convolveBusCave).sliceEvenly(THREAD_COUNT / 2).toTypedArray() +
|
||||||
arrayOf(fadeBus) +
|
arrayOf(fadeBus) +
|
||||||
arrayOf(masterTrack)*/
|
arrayOf(masterTrack)*/
|
||||||
@@ -552,6 +581,7 @@ class AudioMixer : Disposable {
|
|||||||
fun reset() {
|
fun reset() {
|
||||||
ambientStopped = true
|
ambientStopped = true
|
||||||
dynamicTracks.forEach { it.stop() }
|
dynamicTracks.forEach { it.stop() }
|
||||||
|
guiTracks.forEach { it.stop() }
|
||||||
tracks.filter { it.trackType == TrackType.STATIC_SOURCE }.forEach { it.stop() }
|
tracks.filter { it.trackType == TrackType.STATIC_SOURCE }.forEach { it.stop() }
|
||||||
tracks.forEach {
|
tracks.forEach {
|
||||||
it.processor.purgeBuffer()
|
it.processor.purgeBuffer()
|
||||||
@@ -574,6 +604,7 @@ class AudioMixer : Disposable {
|
|||||||
// feedingThread.join()
|
// feedingThread.join()
|
||||||
tracks.forEach { it.tryDispose() }
|
tracks.forEach { it.tryDispose() }
|
||||||
dynamicTracks.forEach { it.tryDispose() }
|
dynamicTracks.forEach { it.tryDispose() }
|
||||||
|
guiTracks.forEach { it.tryDispose() }
|
||||||
masterTrack.tryDispose()
|
masterTrack.tryDispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.torvald.terrarum.audio
|
package net.torvald.terrarum.audio
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Queue
|
|
||||||
import net.torvald.reflection.forceInvoke
|
import net.torvald.reflection.forceInvoke
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.audio.AudioMixer.Companion.DS_FLTIDX_LOW
|
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)
|
bytesRead += read0(buffer, bytesRead)
|
||||||
}
|
}
|
||||||
// if isLooping=true, do gapless sampleRead but reads from itself
|
// 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()
|
track.currentTrack?.reset()
|
||||||
|
|
||||||
bytesRead += read0(buffer, bytesRead)
|
bytesRead += read0(buffer, bytesRead)
|
||||||
|
|||||||
@@ -12,28 +12,32 @@ 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
|
import net.torvald.unsafe.UnsafeHelper
|
||||||
import net.torvald.terrarum.tryDispose
|
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(
|
data class MusicContainer(
|
||||||
|
val toRAM: Boolean = false,
|
||||||
val name: String,
|
val name: String,
|
||||||
val file: File,
|
val file: File,
|
||||||
val loop: Boolean = false,
|
val looping: Boolean = false,
|
||||||
internal var songFinishedHook: (Music) -> Unit = {}
|
internal var songFinishedHook: (Music) -> Unit = {}
|
||||||
): Disposable {
|
): Disposable {
|
||||||
val samplingRate: Int
|
val samplingRate: Int
|
||||||
val codec: String
|
val codec: String
|
||||||
|
|
||||||
var samplesRead = 0L; internal set
|
var samplesReadCount = 0L; internal set
|
||||||
val samplesTotal: Long
|
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 {
|
init {
|
||||||
gdxMusic.isLooping = loop
|
gdxMusic.isLooping = looping
|
||||||
|
|
||||||
gdxMusic.setOnCompletionListener(songFinishedHook)
|
gdxMusic.setOnCompletionListener(songFinishedHook)
|
||||||
|
|
||||||
@@ -82,6 +86,45 @@ data class MusicContainer(
|
|||||||
else -> Long.MAX_VALUE
|
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 {
|
private fun getWavFileSampleCount(file: File): Long {
|
||||||
@@ -133,14 +176,10 @@ 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())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?) = this.file.path == (other as MusicContainer).file.path
|
override fun equals(other: Any?) = this.file.path == (other as MusicContainer).file.path
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
gdxMusic.dispose()
|
gdxMusic.dispose()
|
||||||
|
soundBuf?.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.torvald.terrarum.modulebasegame
|
package net.torvald.terrarum.modulebasegame
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx
|
|
||||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
@@ -148,7 +147,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
|
|||||||
MusicContainer(
|
MusicContainer(
|
||||||
fileHandle.nameWithoutExtension().replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
fileHandle.nameWithoutExtension().replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
|
||||||
fileHandle.file(),
|
fileHandle.file(),
|
||||||
loop = true,
|
looping = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
catch (e: GdxRuntimeException) {
|
catch (e: GdxRuntimeException) {
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
|||||||
}
|
}
|
||||||
|
|
||||||
object UIItemAccessibilityUtil {
|
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() {
|
fun playHapticCursorHovered() {
|
||||||
App.playGUIsound(CommonResourcePool.getAs("sound:haptic_bup"), 0.1666666)
|
App.playGUIsound(CommonResourcePool.getAs("sound:haptic_bup"), 0.1666666)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user