mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-15 13:04:05 +09:00
musicbox wip
This commit is contained in:
@@ -18,6 +18,9 @@ abstract class AudioBank : Disposable {
|
|||||||
abstract val totalSizeInSamples: Long
|
abstract val totalSizeInSamples: Long
|
||||||
abstract fun currentPositionInSamples(): Long
|
abstract fun currentPositionInSamples(): Long
|
||||||
|
|
||||||
|
open fun sendMessage(msg: String) {}
|
||||||
|
open fun sendMessage(bits: Long) {}
|
||||||
|
|
||||||
abstract fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int
|
abstract fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int
|
||||||
abstract fun reset()
|
abstract fun reset()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.audio.audiobank
|
package net.torvald.terrarum.modulebasegame.audio.audiobank
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Queue
|
|
||||||
import net.torvald.terrarum.App
|
import net.torvald.terrarum.App
|
||||||
import net.torvald.terrarum.INGAME
|
import net.torvald.terrarum.INGAME
|
||||||
import net.torvald.terrarum.audio.AudioBank
|
import net.torvald.terrarum.audio.AudioBank
|
||||||
@@ -13,17 +12,17 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {})
|
|||||||
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
|
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 name = "spieluhr"
|
||||||
override val samplingRate = 48000
|
override val samplingRate = 48000 // 122880 // use 122880 to make each tick is 2048 samples
|
||||||
override val channels = 1
|
override val channels = 1
|
||||||
|
|
||||||
private val getSample = // usage: getSample(noteNum 0..60)
|
private val getSample = // usage: getSample(noteNum 0..60)
|
||||||
InstrumentLoader.load("spieluhr", "basegame", "audio/effects/notes/spieluhr.ogg", 29)
|
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
|
private val SAMPLES_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
|
override val totalSizeInSamples = getSample(0).first.size.toLong() // length of lowest-pitch note
|
||||||
|
|
||||||
private val messageQueue = Queue<Msg>()
|
private val messageQueue = ArrayList<Msg>()
|
||||||
|
|
||||||
private fun findSetBits(num: Long): List<Int> {
|
private fun findSetBits(num: Long): List<Int> {
|
||||||
val result = mutableListOf<Int>()
|
val result = mutableListOf<Int>()
|
||||||
@@ -38,7 +37,7 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {})
|
|||||||
/**
|
/**
|
||||||
* Queues the notes such that they are played on the next tick
|
* Queues the notes such that they are played on the next tick
|
||||||
*/
|
*/
|
||||||
fun queuePlay(noteBits: Long) {
|
override fun sendMessage(noteBits: Long) {
|
||||||
if (noteBits == 0L) return
|
if (noteBits == 0L) return
|
||||||
|
|
||||||
val tick = INGAME.WORLD_UPDATE_TIMER + 1
|
val tick = INGAME.WORLD_UPDATE_TIMER + 1
|
||||||
@@ -54,17 +53,34 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {})
|
|||||||
}
|
}
|
||||||
|
|
||||||
// actually queue it
|
// actually queue it
|
||||||
messageQueue.addLast(Msg(tick, buf, buf))
|
messageQueue.add(Msg(tick, buf, buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun currentPositionInSamples(): Long {
|
override fun currentPositionInSamples() = 0L
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int {
|
override fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int {
|
||||||
val tickCount = INGAME.WORLD_UPDATE_TIMER
|
val tickCount = INGAME.WORLD_UPDATE_TIMER
|
||||||
|
val bufferSize = bufferL.size
|
||||||
|
|
||||||
TODO("Not yet implemented")
|
// only copy over the past and current messages
|
||||||
|
messageQueue.filter { it.tick <= tickCount }.forEach {
|
||||||
|
// copy over the samples
|
||||||
|
for (i in 0 until minOf(bufferSize, it.samplesL.size - it.samplesDispatched)) {
|
||||||
|
bufferL[i] += it.samplesL[i + it.samplesDispatched]
|
||||||
|
bufferR[i] += it.samplesR[i + it.samplesDispatched]
|
||||||
|
}
|
||||||
|
|
||||||
|
it.samplesDispatched += bufferSize
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// dequeue the finished messages
|
||||||
|
val messagesToKill = ArrayList<Msg>(messageQueue.filter { it.samplesDispatched >= it.samplesL.size })
|
||||||
|
if (messagesToKill.isNotEmpty()) messagesToKill.forEach {
|
||||||
|
messageQueue.remove(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferSize
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reset() {
|
override fun reset() {
|
||||||
@@ -78,4 +94,23 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {})
|
|||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prel(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): List<Int> {
|
||||||
|
return listOf(
|
||||||
|
n1, n2, n3, n4, n5, n3, n4, n5,
|
||||||
|
n1, n2, n3, n4, n5, n3, n4, n5
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val testNotes = prel(0,0,0,0,0)+
|
||||||
|
prel(24,28,31,36,40) +
|
||||||
|
prel(24, 26,33,28,41) +
|
||||||
|
prel(23,26,31,38,41) +
|
||||||
|
prel(24,28,31,36,40) +
|
||||||
|
prel(24,28,33,40,45) +
|
||||||
|
prel(24,26,30,33,38) +
|
||||||
|
prel(23,26,31,38,43) +
|
||||||
|
prel(23,24,28,31,36) +
|
||||||
|
prel(21,24,28,31,36)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,8 @@ object InstrumentLoader {
|
|||||||
* Will read the sample and create 61 copies of them. Rendered samples will be stored on the CommonResourcePool
|
* 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>
|
* with the naming rule of `"${idBase}_${noteNumber}"`, with format of Pair<FloatArray, FloatArray>
|
||||||
*
|
*
|
||||||
|
* The sample must be in two channels, 48 kHz sampling rate.
|
||||||
|
*
|
||||||
* If `isDualMono` option is set, two values of a pair will point to the same FloatArray.
|
* If `isDualMono` option is set, two values of a pair will point to the same FloatArray.
|
||||||
*
|
*
|
||||||
* @param idBase Base ID string
|
* @param idBase Base ID string
|
||||||
@@ -77,10 +79,11 @@ object InstrumentLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val TAPS = 8
|
private val TAPS = 8
|
||||||
|
private val RESAMPLE_RATE = 1.0// 122880.0 / 48000.0
|
||||||
|
|
||||||
private fun resample(input: FloatArray, output: FloatArray, rate: Double) {
|
private fun resample(input: FloatArray, output: FloatArray, rate: Double) {
|
||||||
for (sampleIdx in 0 until output.size) {
|
for (sampleIdx in 0 until output.size) {
|
||||||
val t = sampleIdx.toDouble() * rate
|
val t = sampleIdx.toDouble() * rate * RESAMPLE_RATE
|
||||||
val leftBound = maxOf(0, (t - TAPS + 1).floorToInt())
|
val leftBound = maxOf(0, (t - TAPS + 1).floorToInt())
|
||||||
val rightBound = minOf(input.size - 1, (t + TAPS).ceilToInt())
|
val rightBound = minOf(input.size - 1, (t + TAPS).ceilToInt())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user