more audio codes

This commit is contained in:
minjaesong
2024-04-14 23:45:34 +09:00
parent 4fba0f70c9
commit 9762f38868
7 changed files with 206 additions and 54 deletions

Binary file not shown.

View File

@@ -512,7 +512,7 @@ public class App implements ApplicationListener {
);
var loadOrder = loadOrderCSVparser.getRecords();
if (loadOrder.size() > 0) {
if (!loadOrder.isEmpty()) {
var modname = loadOrder.get(0).get(0);
var textureFile = Gdx.files.internal("assets/mods/"+modname+"/splashback.png");
@@ -531,8 +531,8 @@ public class App implements ApplicationListener {
}
finally {
try {loadOrderCSVparser.close();}
catch (IOException e) {}
try { loadOrderCSVparser.close(); }
catch (IOException | NullPointerException e) {}
}
}
@@ -740,12 +740,15 @@ public class App implements ApplicationListener {
FrameBufferManager.end();
screenshotRequested = false;
Terrarum.INSTANCE.getIngame().sendNotification(msg);
var ingame = Terrarum.INSTANCE.getIngame();
if (ingame != null) ingame.sendNotification(msg);
}
}
public static Texture getCurrentDitherTex() {
int hash = (int) (31 + GLOBAL_RENDER_TIMER + 0x165667B1 + GLOBAL_RENDER_TIMER * 0xC2B2AE3D);
var T = (int) GLOBAL_RENDER_TIMER;
int hash = 31 + T + 0x165667B1 + T * 0xC2B2AE3D;
hash = Integer.rotateLeft(hash, 17) * 0x27D4EB2F;
hash ^= hash >>> 15;
hash *= 0x85EBCA77;
@@ -1102,12 +1105,7 @@ public class App implements ApplicationListener {
}
// nullify if not actually connected
try {
if (!((XinputControllerAdapter) gamepad).getC().isConnected()) {
gamepad = null;
}
}
catch (NullPointerException notQuiteWindows) {
if (gamepad != null && !((XinputControllerAdapter) gamepad).getC().isConnected()) {
gamepad = null;
}
}
@@ -1946,7 +1944,7 @@ public class App implements ApplicationListener {
public static void addDebugTime(String target, String... targets) {
long l = 0L;
for (String s : targets) {
l += ((long) debugTimers.get(s));
l += debugTimers.get(s);
}
debugTimers.put(target, l);
}

View File

@@ -343,7 +343,11 @@ Sound from <https://freesound.org/people/Benboncan/>
- effects/haptic_*.ogg
℗ 2024 CuriousTorvald
My own recordings
Sound from <https://freesound.org/people/CuriousTorvald/>
- effects/notes/spieluhr.ogg
℗ 2013 Ubikphonik
Sound from <https://freesound.org/people/UbikPhonik/>
$BULLET Impulse Responses:

View File

@@ -14,6 +14,9 @@ import java.io.File
*/
object AudioHelper {
/**
* The audio must be in stereo (two channels)
*/
fun getIR(module: String, path: String): Array<ComplexArray> {
val id = "convolution$$module.$path"
@@ -66,6 +69,9 @@ object AudioHelper {
return Array(2) { FFT.fft(conv[it]) }
}
/**
* The audio must be in stereo
*/
fun getAudioInSamples(module: String, path: String): Array<FloatArray> {
val id = "audiosamplesf32$$module.$path"

View File

@@ -1,41 +0,0 @@
package net.torvald.terrarum.audio.audiobank
import com.badlogic.gdx.utils.Queue
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.audio.AudioBank
/**
* Created by minjaesong on 2024-04-12.
*/
class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) : AudioBank() {
override val name = "spieluhr"
override val samplingRate = 48000
override val channels = 1
override val totalSizeInSamples = Long.MAX_VALUE // TODO length of lowest-pitch note
private val messageQueue = Queue<Pair<Long, Long>>() // pair of: absolute tick count, notes (61 notes polyphony)
override fun currentPositionInSamples(): Long {
TODO("Not yet implemented")
}
override fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int {
val tickCount = INGAME.WORLD_UPDATE_TIMER
TODO("Not yet implemented")
}
override fun reset() {
TODO("Not yet implemented")
}
override fun makeCopy(): AudioBank {
TODO("Not yet implemented")
}
override fun dispose() {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,81 @@
package net.torvald.terrarum.modulebasegame.audio.audiobank
import com.badlogic.gdx.utils.Queue
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.audio.AudioBank
/**
* Created by minjaesong on 2024-04-12.
*/
class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {}) : AudioBank() {
private data class Msg(val tick: Long, val samplesL: FloatArray, val samplesR: FloatArray, var samplesDispatched: Int = 0) // in many cases, samplesL and samplesR will point to the same object
override val name = "spieluhr"
override val samplingRate = 48000
override val channels = 1
private val getSample = // usage: getSample(noteNum 0..60)
InstrumentLoader.load("spieluhr", "basegame", "audio/effects/notes/spieluhr.ogg", 29)
private val SAMEPLES_PER_TICK = samplingRate / App.TICK_SPEED // should be 800 on default setting
override val totalSizeInSamples = getSample(0).first.size.toLong() // length of lowest-pitch note
private val messageQueue = Queue<Msg>()
private fun findSetBits(num: Long): List<Int> {
val result = mutableListOf<Int>()
for (i in 0 until 61) {
if (num and (1L shl i) != 0L) {
result.add(i)
}
}
return result
}
/**
* Queues the notes such that they are played on the next tick
*/
fun queuePlay(noteBits: Long) {
if (noteBits == 0L) return
val tick = INGAME.WORLD_UPDATE_TIMER + 1
val notes = findSetBits(noteBits)
val buf = FloatArray(getSample(notes.first()).first.size)
// combine all those samples
notes.forEach { note ->
getSample(note).first.forEachIndexed { index, fl ->
buf[index] += fl
}
}
// actually queue it
messageQueue.addLast(Msg(tick, buf, buf))
}
override fun currentPositionInSamples(): Long {
TODO("Not yet implemented")
}
override fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int {
val tickCount = INGAME.WORLD_UPDATE_TIMER
TODO("Not yet implemented")
}
override fun reset() {
TODO("Not yet implemented")
}
override fun makeCopy(): AudioBank {
TODO("Not yet implemented")
}
override fun dispose() {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,101 @@
package net.torvald.terrarum.modulebasegame.audio.audiobank
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.audio.AudioProcessBuf
import net.torvald.terrarum.audio.audiobank.MusicContainer
import net.torvald.terrarum.ceilToInt
import net.torvald.terrarum.floorToInt
import java.lang.Math.pow
import kotlin.math.roundToInt
/**
* Creates 61-note (C1 to C5) pack of samples from a single file. Tuning system is 12-note Equal Temperament.
*
* Created by minjaesong on 2024-04-14.
*/
object InstrumentLoader {
// 0 is C0
private fun getStretch(noteNum: Int) = pow(2.0, (noteNum - 29) / 12.0)
/**
* Will read the sample and create 61 copies of them. Rendered samples will be stored on the CommonResourcePool
* with the naming rule of `"${idBase}_${noteNumber}"`, with format of Pair<FloatArray, FloatArray>
*
* If `isDualMono` option is set, two values of a pair will point to the same FloatArray.
*
* @param idBase Base ID string
* @param module Which module the path must refer to
* @param path path to the audio
* @param initialNote Initial note of the given sample. Ranged from 0 to 60. C1 is 0, F3 is 29
* @param isDualMono if the input sample is in dual mono
*/
fun load(idBase: String, module: String, path: String, initialNote: Int, isDualMono: Boolean = true): (Int) -> Pair<FloatArray, FloatArray> {
if (initialNote !in 0..60) throw IllegalArgumentException("Initial note too low or high ($initialNote not in range of 0..60)")
val baseResourceName = "inst$$idBase"
if (CommonResourcePool.resourceExists("${baseResourceName}_$initialNote")) return { it ->
CommonResourcePool.getAs<Pair<FloatArray, FloatArray>>("${baseResourceName}_$it")
}
val masterFile = MusicContainer("${idBase}_${initialNote}", ModMgr.getFile(module, path))
val masterSamplesL = FloatArray(masterFile.totalSizeInSamples.toInt())
val masterSamplesR = FloatArray(masterFile.totalSizeInSamples.toInt())
masterFile.readSamples(masterSamplesL, masterSamplesR)
val renderedSamples = Array<Pair<FloatArray?, FloatArray?>>(61) { null to null }
for (j in 0 until 61) {
val i = j - initialNote
val rate = getStretch(i)
val sampleCount = (masterFile.totalSizeInSamples * rate).roundToInt()
val samplesL = FloatArray(sampleCount)
val samplesR = if (isDualMono) samplesL else FloatArray(sampleCount)
renderedSamples[j] = samplesL to samplesR
// do resampling
resample(masterSamplesL, samplesL, rate)
if (!isDualMono)
resample(masterSamplesR, samplesR, rate)
CommonResourcePool.addToLoadingList("${baseResourceName}_$j") {
samplesL to samplesR
}
}
CommonResourcePool.loadAll()
masterFile.dispose()
return { it ->
CommonResourcePool.getAs<Pair<FloatArray, FloatArray>>("${baseResourceName}_$it")
}
}
private val TAPS = 8
private fun resample(input: FloatArray, output: FloatArray, rate: Double) {
for (sampleIdx in 0 until output.size) {
val t = sampleIdx.toDouble() * rate
val leftBound = maxOf(0, (t - TAPS + 1).floorToInt())
val rightBound = minOf(input.size - 1, (t + TAPS).ceilToInt())
var akkuL = 0.0
var weightedSum = 0.0
for (j in leftBound..rightBound) {
val w = AudioProcessBuf.L(t - j.toDouble())
akkuL += input[j] * w
weightedSum += w
}
output[sampleIdx] = (akkuL / weightedSum).toFloat()
}
}
}