mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
audio device changes
This commit is contained in:
@@ -5,6 +5,23 @@ import net.torvald.tsvm.peripheral.AudioAdapter
|
||||
import net.torvald.tsvm.peripheral.MP2Env
|
||||
|
||||
/**
|
||||
* Each playhead is separate OpenAL device with its own PCM sample buffers.
|
||||
* Media decoders (MP2, TAD) are independent to the playheads and there is only one.
|
||||
*
|
||||
* NOTES:
|
||||
* 1. tracker mode is currently unimplemented.
|
||||
* 2. PCM upload buffer (accessed by `putPcmDataByPtr`) is shared between four playheads
|
||||
*
|
||||
* ## How to upload PCM audio into a playhead
|
||||
*
|
||||
* 1. prepare PCM data
|
||||
* 2. queue up PCM data by `audio.putPcmDataByPtr(pcmDataPtr, pcmDataLength, playhead)`
|
||||
* 3. specify PCM upload length by `audio.setSampleUploadLength(playhead, pcmDataLength)`
|
||||
* 4. start uploading `audio.startSampleUpload(playhead)`
|
||||
* 5. sample will be ready after a few microseconds.
|
||||
*
|
||||
* Uploaded samples will be queued by the playhead for gapless playback
|
||||
*
|
||||
* Created by minjaesong on 2022-12-31.
|
||||
*/
|
||||
class AudioJSR223Delegate(private val vm: VM) {
|
||||
@@ -51,16 +68,16 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
fun setTickRate(playhead: Int, rate: Int) { getPlayhead(playhead)?.tickRate = rate and 255 }
|
||||
fun getTickRate(playhead: Int) = getPlayhead(playhead)?.tickRate
|
||||
|
||||
fun putPcmDataByPtr(ptr: Int, length: Int, destOffset: Int) {
|
||||
fun putPcmDataByPtr(playhead: Int, ptr: Int, length: Int, destOffset: Int) {
|
||||
getFirstSnd()?.let {
|
||||
val vkMult = if (ptr >= 0) 1 else -1
|
||||
for (k in 0L until length) {
|
||||
val vk = k * vkMult
|
||||
it.pcmBin[k + destOffset] = vm.peek(ptr + vk)!!
|
||||
it.pcmBin[playhead][k + destOffset] = vm.peek(ptr + vk)!!
|
||||
}
|
||||
}
|
||||
}
|
||||
fun getPcmData(index: Int) = getFirstSnd()?.pcmBin?.get(index.toLong())
|
||||
fun getPcmData(playhead: Int, index: Int) = getFirstSnd()?.pcmBin?.get(playhead)?.get(index.toLong())
|
||||
|
||||
fun setPcmQueueCapacityIndex(playhead: Int, index: Int) { getPlayhead(playhead)?.pcmQueueSizeIndex = index }
|
||||
fun getPcmQueueCapacityIndex(playhead: Int) { getPlayhead(playhead)?.pcmQueueSizeIndex }
|
||||
|
||||
@@ -763,7 +763,7 @@ class VM(
|
||||
else if (dev is AudioAdapter) {
|
||||
if (relPtrInDev(fromRel, len, 64, 2367)) dev.mediaDecodedBin.ptr + fromRel - 64
|
||||
else if (relPtrInDev(fromRel, len, 2368, 4096)) dev.mediaFrameBin.ptr + fromRel - 2368
|
||||
else if (relPtrInDev(fromRel, len, 65536, 131072)) dev.pcmBin.ptr + fromRel - 65536
|
||||
else if (relPtrInDev(fromRel, len, 65536, 131072)) dev.pcmBin[dev.selectedPcmBin].ptr + fromRel - 65536
|
||||
else null
|
||||
}
|
||||
else if (dev is GraphicsAdapter) {
|
||||
|
||||
@@ -122,13 +122,20 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
internal val playdata = Array(256) { Array(64) { TaudPlayData(0,0,0,0,0,0,0,0) } }
|
||||
internal val playheads: Array<Playhead>
|
||||
internal val cueSheet = Array(2048) { PlayCue() }
|
||||
internal val pcmBin = UnsafeHelper.allocate(65536L, this)
|
||||
internal val pcmBin = arrayOf(
|
||||
UnsafeHelper.allocate(65536L, this),
|
||||
UnsafeHelper.allocate(65536L, this),
|
||||
UnsafeHelper.allocate(65536L, this),
|
||||
UnsafeHelper.allocate(65536L, this),
|
||||
)
|
||||
|
||||
internal val mediaFrameBin = UnsafeHelper.allocate(1728, this)
|
||||
internal val mediaDecodedBin = UnsafeHelper.allocate(2304, this)
|
||||
|
||||
@Volatile private var mp2Busy = false
|
||||
|
||||
@Volatile var selectedPcmBin = 0
|
||||
|
||||
// TAD (Terrarum Advanced Audio) decoder buffers
|
||||
internal val tadInputBin = UnsafeHelper.allocate(65536L, this) // Input: compressed TAD chunk (max 64KB)
|
||||
internal val tadDecodedBin = UnsafeHelper.allocate(65536L, this) // Output: PCMu8 stereo (32768 samples * 2 channels)
|
||||
@@ -241,7 +248,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
|
||||
renderRunnables = Array(4) { RenderRunnable(playheads[it]) }
|
||||
renderThreads = Array(4) { Thread(renderThreadGroup, renderRunnables[it], "AudioRenderHead${it+1}!$hash") }
|
||||
writeQueueingRunnables = Array(4) { WriteQueueingRunnable(playheads[it], pcmBin) }
|
||||
writeQueueingRunnables = Array(4) { WriteQueueingRunnable(playheads[it], pcmBin[it]) }
|
||||
writeQueueingThreads = Array(4) { Thread(writeQueueingGroup, writeQueueingRunnables[it], "AudioQueueingHead${it+1}!$hash") }
|
||||
|
||||
// printdbg("AudioAdapter latency: ${audioDevice.latency}")
|
||||
@@ -315,13 +322,14 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
42 -> -1 // TAD control (write-only)
|
||||
43 -> tadQuality.toByte()
|
||||
44 -> tadBusy.toInt().toByte()
|
||||
45 -> selectedPcmBin.toByte()
|
||||
in 64..2367 -> mediaDecodedBin[addr - 64]
|
||||
in 2368..4095 -> mediaFrameBin[addr - 2368]
|
||||
in 4096..4097 -> 0
|
||||
in 32768..65535 -> (adi - 32768).let {
|
||||
cueSheet[it / 16].read(it % 15)
|
||||
}
|
||||
in 65536..131071 -> pcmBin[addr - 65536]
|
||||
in 65536..131071 -> pcmBin[selectedPcmBin][addr - 65536]
|
||||
else -> {
|
||||
println("[AudioAdapter] Bus mirroring on mmio_reading while trying to read address $addr")
|
||||
mmio_read(addr % 131072)
|
||||
@@ -349,12 +357,13 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
// TAD quality (0-5)
|
||||
tadQuality = bi.coerceIn(0, 5)
|
||||
}
|
||||
45 -> selectedPcmBin = bi % 4
|
||||
in 64..2367 -> { mediaDecodedBin[addr - 64] = byte }
|
||||
in 2368..4095 -> { mediaFrameBin[addr - 2368] = byte }
|
||||
in 32768..65535 -> { (adi - 32768).let {
|
||||
cueSheet[it / 16].write(it % 15, bi)
|
||||
} }
|
||||
in 65536..131071 -> { pcmBin[addr - 65536] = byte }
|
||||
in 65536..131071 -> { pcmBin[selectedPcmBin][addr - 65536] = byte }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +377,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
writeQueueingGroup.interrupt()
|
||||
playheads.forEach { it.dispose() }
|
||||
sampleBin.destroy()
|
||||
pcmBin.destroy()
|
||||
pcmBin.forEach { it.destroy() }
|
||||
mediaFrameBin.destroy()
|
||||
mediaDecodedBin.destroy()
|
||||
tadInputBin.destroy()
|
||||
|
||||
Reference in New Issue
Block a user