mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 10:04:05 +09:00
more audio codes
This commit is contained in:
BIN
assets/mods/basegame/audio/effects/notes/spieluhr.ogg
LFS
Normal file
BIN
assets/mods/basegame/audio/effects/notes/spieluhr.ogg
LFS
Normal file
Binary file not shown.
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user