MusicContainer extracted out of the modulebasegame

This commit is contained in:
minjaesong
2024-02-10 16:09:00 +09:00
parent ea940e93ba
commit b93e987011
11 changed files with 143 additions and 144 deletions

View File

@@ -10,10 +10,8 @@ import com.badlogic.gdx.utils.JsonValue
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.reflection.extortField import net.torvald.reflection.extortField
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.audio.* import net.torvald.terrarum.audio.*
import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.PlaysMusic import net.torvald.terrarum.modulebasegame.gameactors.PlaysMusic
import net.torvald.terrarum.ui.BasicDebugInfoWindow import net.torvald.terrarum.ui.BasicDebugInfoWindow

View File

@@ -6,5 +6,5 @@ entrypoint=net.torvald.terrarum.musicplayer.EntryPoint
releasedate=2024-02-10 releasedate=2024-02-10
version=1.0.0 version=1.0.0
jar=MusicPlayer.jar jar=MusicPlayer.jar
jarhash=7cadf1c1ffb8c87563413cabd3bab08c5111b045a7f02e770888188bca576445 jarhash=3b8db936a76ffbd3fb4909de9582254e2ffc6eab7c4587ced0a738b21d05202a
dependency=basegame 0.4.0 dependency=basegame 0.4.0

View File

@@ -1221,7 +1221,7 @@ public class App implements ApplicationListener {
audioBufferSize = getConfigInt("audio_buffer_size"); audioBufferSize = getConfigInt("audio_buffer_size");
audioMixer = new AudioMixer(audioBufferSize); audioMixer = new AudioMixer();
audioMixerInitialised = true; audioMixerInitialised = true;
audioManagerThread = new Thread(new AudioManagerRunnable(audioMixer), "TerrarumAudioManager"); audioManagerThread = new Thread(new AudioManagerRunnable(audioMixer), "TerrarumAudioManager");
audioManagerThread.setPriority(MAX_PRIORITY); // higher = more predictable; audio delay is very noticeable so it gets high priority audioManagerThread.setPriority(MAX_PRIORITY); // higher = more predictable; audio delay is very noticeable so it gets high priority
@@ -1257,7 +1257,7 @@ public class App implements ApplicationListener {
audioMixer.dispose(); audioMixer.dispose();
audioBufferSize = bufferSize; audioBufferSize = bufferSize;
audioMixer = new AudioMixer(audioBufferSize); audioMixer = new AudioMixer();
// paste music tracks // paste music tracks
for (int i = 0; i < audioMixer.getDynamicTracks().length; i++) { for (int i = 0; i < audioMixer.getDynamicTracks().length; i++) {

View File

@@ -2,7 +2,6 @@ package net.torvald.terrarum.audio
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.files.FileHandle
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.tryDispose import net.torvald.terrarum.tryDispose
/** /**

View File

@@ -13,7 +13,6 @@ import net.torvald.terrarum.audio.dsp.*
import net.torvald.terrarum.concurrent.ThreadExecutor import net.torvald.terrarum.concurrent.ThreadExecutor
import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.modulebasegame.BuildingMaker import net.torvald.terrarum.modulebasegame.BuildingMaker
import net.torvald.terrarum.modulebasegame.MusicContainer
import java.lang.Thread.MAX_PRIORITY import java.lang.Thread.MAX_PRIORITY
import java.util.* import java.util.*
import kotlin.math.* import kotlin.math.*
@@ -23,7 +22,7 @@ import kotlin.math.*
* *
* Created by minjaesong on 2023-11-07. * Created by minjaesong on 2023-11-07.
*/ */
class AudioMixer(val bufferSize: Int): Disposable { class AudioMixer : Disposable {
companion object { companion object {
const val SPEED_OF_SOUND_AIR = 340f const val SPEED_OF_SOUND_AIR = 340f

View File

@@ -0,0 +1,135 @@
package net.torvald.terrarum.audio
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.audio.Music
import com.badlogic.gdx.backends.lwjgl3.audio.Mp3
import com.badlogic.gdx.backends.lwjgl3.audio.Ogg
import com.badlogic.gdx.backends.lwjgl3.audio.OggInputStream
import com.badlogic.gdx.backends.lwjgl3.audio.Wav
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 java.io.File
import java.io.FileInputStream
import javax.sound.sampled.AudioSystem
data class MusicContainer(
val name: String,
val file: File,
val gdxMusic: Music,
internal var songFinishedHook: (Music) -> Unit = {}
) {
val samplingRate: Int
val codec: String
var samplesRead = 0L; internal set
val samplesTotal: Long
init {
gdxMusic.setOnCompletionListener(songFinishedHook)
samplingRate = when (gdxMusic) {
is Wav.Music -> {
val rate = gdxMusic.extortField<Wav.WavInputStream>("input")!!.sampleRate
App.printdbg(this, "music $name is WAV; rate = $rate")
rate
}
is Ogg.Music -> {
val rate = gdxMusic.extortField<OggInputStream>("input")!!.sampleRate
App.printdbg(this, "music $name is OGG; rate = $rate")
rate
}
is Mp3.Music -> {
val tempMusic = Gdx.audio.newMusic(Gdx.files.absolute(file.absolutePath))
val bitstream = tempMusic.extortField<Bitstream>("bitstream")!!
val header = bitstream.readFrame()
val rate = header.sampleRate
tempMusic.dispose()
// val bitstream = gdxMusic.extortField<Bitstream>("bitstream")!!
// val header = bitstream.readFrame()
// val rate = header.sampleRate
// gdxMusic.reset()
App.printdbg(this, "music $name is MP3; rate = $rate")
rate
}
else -> {
App.printdbg(this, "music $name is ${gdxMusic::class.qualifiedName}; rate = default")
TerrarumAudioMixerTrack.SAMPLING_RATE
}
}
codec = gdxMusic::class.qualifiedName!!.split('.').let {
if (it.last() == "Music") it.dropLast(1).last() else it.last()
}
samplesTotal = when (gdxMusic) {
is Wav.Music -> getWavFileSampleCount(file)
is Ogg.Music -> getOggFileSampleCount(file)
is Mp3.Music -> getMp3FileSampleCount(file)
else -> Long.MAX_VALUE
}
}
private fun getWavFileSampleCount(file: File): Long {
return try {
val ais = AudioSystem.getAudioInputStream(file)
val r = ais.frameLength
ais.close()
r
}
catch (e: Throwable) {
Long.MAX_VALUE
}
}
private fun getOggFileSampleCount(file: File): Long {
return try {
val vorbisFile = VorbisFile(file.absolutePath)
vorbisFile.pcm_total(0)
}
catch (e: Throwable) {
Long.MAX_VALUE
}
}
private fun getMp3FileSampleCount(file: File): Long {
return try {
val fis = FileInputStream(file)
val bs = Bitstream(fis)
var header = bs.readFrame()
val rate = header.frequency()
var totalSamples = 0L
while (header != null) {
totalSamples += (header.ms_per_frame() * rate / 1000).toLong()
bs.closeFrame()
header = bs.readFrame()
}
bs.close()
fis.close()
totalSamples
}
catch (_: Throwable) {
Long.MAX_VALUE
}
}
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
}

View File

@@ -10,7 +10,6 @@ import net.torvald.terrarum.audio.dsp.TerrarumAudioFilter
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.getHashStr import net.torvald.terrarum.getHashStr
import net.torvald.terrarum.hashStrMap import net.torvald.terrarum.hashStrMap
import net.torvald.terrarum.modulebasegame.MusicContainer
import kotlin.math.log10 import kotlin.math.log10
import kotlin.math.pow import kotlin.math.pow

View File

@@ -4,9 +4,9 @@ import net.torvald.random.HQRNG
import net.torvald.terrarum.App import net.torvald.terrarum.App
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.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
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.savegame.toBigEndian import net.torvald.terrarum.savegame.toBigEndian

View File

@@ -1,145 +1,14 @@
package net.torvald.terrarum.modulebasegame package net.torvald.terrarum.modulebasegame
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.audio.Music
import com.badlogic.gdx.backends.lwjgl3.audio.Mp3
import com.badlogic.gdx.backends.lwjgl3.audio.Ogg
import com.badlogic.gdx.backends.lwjgl3.audio.OggInputStream
import com.badlogic.gdx.backends.lwjgl3.audio.Wav
import com.badlogic.gdx.backends.lwjgl3.audio.Wav.WavInputStream
import com.badlogic.gdx.utils.GdxRuntimeException import com.badlogic.gdx.utils.GdxRuntimeException
import com.jcraft.jorbis.VorbisFile
import com.jme3.math.FastMath import com.jme3.math.FastMath
import javazoom.jl.decoder.Bitstream
import net.torvald.reflection.extortField
import net.torvald.reflection.forceInvoke
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.AudioMixer import net.torvald.terrarum.audio.AudioMixer
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE import net.torvald.terrarum.audio.MusicContainer
import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH
import java.io.File import java.io.File
import java.io.FileInputStream
import javax.sound.sampled.AudioSystem
data class MusicContainer(
val name: String,
val file: File,
val gdxMusic: Music,
internal var songFinishedHook: (Music) -> Unit = {}
) {
val samplingRate: Int
val codec: String
var samplesRead = 0L; internal set
val samplesTotal: Long
init {
gdxMusic.setOnCompletionListener(songFinishedHook)
samplingRate = when (gdxMusic) {
is Wav.Music -> {
val rate = gdxMusic.extortField<WavInputStream>("input")!!.sampleRate
printdbg(this, "music $name is WAV; rate = $rate")
rate
}
is Ogg.Music -> {
val rate = gdxMusic.extortField<OggInputStream>("input")!!.sampleRate
printdbg(this, "music $name is OGG; rate = $rate")
rate
}
is Mp3.Music -> {
val tempMusic = Gdx.audio.newMusic(Gdx.files.absolute(file.absolutePath))
val bitstream = tempMusic.extortField<Bitstream>("bitstream")!!
val header = bitstream.readFrame()
val rate = header.sampleRate
tempMusic.dispose()
// val bitstream = gdxMusic.extortField<Bitstream>("bitstream")!!
// val header = bitstream.readFrame()
// val rate = header.sampleRate
// gdxMusic.reset()
printdbg(this, "music $name is MP3; rate = $rate")
rate
}
else -> {
printdbg(this, "music $name is ${gdxMusic::class.qualifiedName}; rate = default")
SAMPLING_RATE
}
}
codec = gdxMusic::class.qualifiedName!!.split('.').let {
if (it.last() == "Music") it.dropLast(1).last() else it.last()
}
samplesTotal = when (gdxMusic) {
is Wav.Music -> getWavFileSampleCount(file)
is Ogg.Music -> getOggFileSampleCount(file)
is Mp3.Music -> getMp3FileSampleCount(file)
else -> Long.MAX_VALUE
}
}
private fun getWavFileSampleCount(file: File): Long {
return try {
val ais = AudioSystem.getAudioInputStream(file)
val r = ais.frameLength
ais.close()
r
}
catch (e: Throwable) {
Long.MAX_VALUE
}
}
private fun getOggFileSampleCount(file: File): Long {
return try {
val vorbisFile = VorbisFile(file.absolutePath)
vorbisFile.pcm_total(0)
}
catch (e: Throwable) {
Long.MAX_VALUE
}
}
private fun getMp3FileSampleCount(file: File): Long {
return try {
val fis = FileInputStream(file)
val bs = Bitstream(fis)
var header = bs.readFrame()
val rate = header.frequency()
var totalSamples = 0L
while (header != null) {
totalSamples += (header.ms_per_frame() * rate / 1000).toLong()
bs.closeFrame()
header = bs.readFrame()
}
bs.close()
fis.close()
totalSamples
}
catch (_: Throwable) {
Long.MAX_VALUE
}
}
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
}
class TerrarumMusicGovernor : MusicGovernor() { class TerrarumMusicGovernor : MusicGovernor() {
private val STATE_INIT = 0 private val STATE_INIT = 0

View File

@@ -11,6 +11,7 @@ import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN
import net.torvald.terrarum.audio.MusicContainer
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
import net.torvald.terrarum.audio.dsp.NullFilter import net.torvald.terrarum.audio.dsp.NullFilter
import net.torvald.terrarum.audio.dsp.Phono import net.torvald.terrarum.audio.dsp.Phono
@@ -20,7 +21,6 @@ import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Lightbox import net.torvald.terrarum.gameactors.Lightbox
import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.modulebasegame.TerrarumMusicGovernor import net.torvald.terrarum.modulebasegame.TerrarumMusicGovernor
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef

View File

@@ -5,12 +5,12 @@ import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.audio.AudioMixer import net.torvald.terrarum.audio.AudioMixer
import net.torvald.terrarum.audio.MusicContainer
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.MusicContainer
import net.torvald.terrarum.modulebasegame.TerrarumMusicGovernor import net.torvald.terrarum.modulebasegame.TerrarumMusicGovernor
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef import net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef