audio adapter now takes ui8 bytes directly

This commit is contained in:
minjaesong
2023-01-01 18:14:43 +09:00
parent 7d55827551
commit 77ff9c12ec
8 changed files with 214 additions and 200 deletions

View File

@@ -96,43 +96,9 @@ function readBytes(length) {
return ptr
}
/*let sampleSize = FILE_SIZE
const FETCH_INTERVAL = 631578947
let updateAkku = FETCH_INTERVAL
let oldNanoTime = sys.nanoTime()
const BLOCK_SIZE = 37894
audio.setPcmMode(0)
audio.setMasterVolume(0, 255)
while (sampleSize > 0) {
let newNanoTime = sys.nanoTime()
updateAkku += newNanoTime - oldNanoTime
oldNanoTime = newNanoTime
if (updateAkku >= FETCH_INTERVAL) {
println((FILE_SIZE - sampleSize) / FILE_SIZE * 100 + "%")
updateAkku -= FETCH_INTERVAL
let readLength = (sampleSize < BLOCK_SIZE) ? sampleSize : BLOCK_SIZE
let samples = readBytes(readLength)
audio.setUploadLength(0, readLength)
audio.putPcmDataByPtr(samples, readLength, 0)
audio.play(0)
sampleSize -= readLength
sys.free(samples)
}
sys.spin()
}*/
let sampleSize = FILE_SIZE
const BLOCK_SIZE = 4096
const QUEUEING_SIZE = 4
const QUEUE_MAX = 4 // according to the spec
audio.resetParams(0)
audio.purgeQueue(0)
@@ -145,20 +111,23 @@ audio.setMasterVolume(0, 255)
while (sampleSize > 0) {
let queueSize = audio.getPosition(0)
serial.println(`[js] Trying to upload samples, queueSize = ${queueSize}`)
// serial.println(`[js] Trying to upload samples, queueSize = ${queueSize}`)
print(".")
if (queueSize == 0) {
if (queueSize <= 1) {
serial.println(`[js] Queue size: ${queueSize}; uploading ${QUEUE_MAX - queueSize} samples`)
println()
println((FILE_SIZE - sampleSize) / FILE_SIZE * 100 + " %")
// upload four samples for lag-safely
for (let repeat = QUEUEING_SIZE; repeat > 0; repeat--) {
for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) {
let readLength = (sampleSize < BLOCK_SIZE) ? sampleSize : BLOCK_SIZE
let samples = readBytes(readLength)
audio.putPcmDataByPtr(samples, readLength, 0)
audio.uploadSamples(0, readLength)
audio.setSampleUploadLength(0, readLength)
audio.startSampleUpload(0)
sampleSize -= readLength
sys.free(samples)
@@ -169,6 +138,9 @@ while (sampleSize > 0) {
audio.play(0)
}
// audio.setMasterVolume(0, (Math.random()*255)|0)
sys.sleep(10)
}

View File

@@ -502,6 +502,27 @@ shell.coreutils = {
}
else return 1
},
mv: function(args) {
if (args[2] === undefined || args[1] === undefined) {
printerrln(`Usage: ${args[0].toUpperCase()} source_file destination_file`)
return
}
let path = shell.resolvePathInput(args[1])
let pathd = shell.resolvePathInput(args[2])
let sourceFile = files.open(path.full)
let destFile = files.open(pathd.full)
debugprintln(`[cp] source path: ${path.full}`)
debugprintln(`[cp] dest path: ${pathd.full}`)
if (sourceFile.isDirectory || !sourceFile.exists) { printerrln(`${args[0].toUpperCase()} failed for '${sourceFile.fullPath}'`); return 1 } // if file is directory or failed to open, IO error code will be returned
if (destFile.isDirectory) { printerrln(`${args[0].toUpperCase()} failed for '${destFile.fullPath}'`); return 1 } // if file is directory or failed to open, IO error code will be returned
destFile.bwrite(sourceFile.bread())
destFile.flush(); destFile.close()
sourceFile.remove()
},
rem: function(args) {
return 0
},
@@ -555,6 +576,7 @@ shell.coreutils.rm = shell.coreutils.del
shell.coreutils.ls = shell.coreutils.dir
shell.coreutils.time = shell.coreutils.date
shell.coreutils.md = shell.coreutils.mkdir
shell.coreutils.move = shell.coreutils.mv
// end of command aliases
Object.freeze(shell.coreutils)
shell.stdio = {

View File

@@ -573,7 +573,7 @@ Play Head Flags
NOTE: changing from PCM mode to Tracker mode or vice versa will also reset the parameters as described above
Byte 2
- PCM Mode: Sampling rate multiplier in 3.5 Unsigned Minifloat (0.03125x to 126x)
- PCM Mode: Write non-zero value to start uploading; always 0 when read
Byte 3 (Tracker Mode)
- BPM (24 to 280. Play Data will change this register)
@@ -583,6 +583,8 @@ Play Head Flags
Byte 3-4 (PCM Mode)
- Signed Int16 Sampling rate difference from 30000 Hz
Uploaded PCM data will be stored onto the queue and the queue is only 4-entries long; any more uploads will be silently discarded.
32768..65535 RW: Cue Sheet (2048 cues)
Byte 1..15: pattern number for voice 1..15

View File

@@ -16,7 +16,10 @@ class AudioJSR223Delegate(private val vm: VM) {
fun setTrackerMode(playhead: Int) { getPlayhead(playhead)?.isPcmMode = false }
fun isTrackerMode(playhead: Int) = getPlayhead(playhead)?.isPcmMode == false
fun setMasterVolume(playhead: Int, volume: Int) { getPlayhead(playhead)?.masterVolume = volume and 255 }
fun setMasterVolume(playhead: Int, volume: Int) { getPlayhead(playhead)?.apply {
masterVolume = volume and 255
audioDevice.setVolume(masterVolume / 255f)
} }
fun getMasterVolume(playhead: Int) = getPlayhead(playhead)?.masterVolume
fun setMasterPan(playhead: Int, pan: Int) { getPlayhead(playhead)?.masterPan = pan and 255 }
@@ -29,13 +32,12 @@ class AudioJSR223Delegate(private val vm: VM) {
// fun setPosition(playhead: Int, pos: Int) { getPlayhead(playhead)?.position = pos and 65535 }
fun getPosition(playhead: Int) = getPlayhead(playhead)?.position
fun uploadSamples(playhead: Int, length: Int) { getPlayhead(playhead)?.pcmUploadLength = length and 65535 }
fun setSampleUploadLength(playhead: Int, length: Int) { getPlayhead(playhead)?.pcmUploadLength = length and 65535 }
fun setSamplingRate(playhead: Int, rate: Int) { getPlayhead(playhead)?.setSamplingRate(rate) }
fun getSamplingRate(playhead: Int) = getPlayhead(playhead)?.getSamplingRate()
fun setSamplingRateMult(playhead: Int, mult: Float) { getPlayhead(playhead)?.samplingRateMult = ThreeFiveMiniUfloat(mult) }
fun getSamplingRateMult(playhead: Int) = getPlayhead(playhead)?.samplingRateMult?.toFloat()
fun startSampleUpload(playhead: Int) { getPlayhead(playhead)?.pcmUpload = true }
fun setBPM(playhead: Int, bpm: Int) { getPlayhead(playhead)?.bpm = (bpm - 24).and(255) + 24 }
fun getBPM(playhead: Int) = getPlayhead(playhead)?.bpm

View File

@@ -3,13 +3,15 @@ package net.torvald
import sun.misc.Unsafe
import java.io.PrintStream
class DanglingPointerException(msg: String) : NullPointerException(msg)
class AddressOverflowException(msg: String) : IndexOutOfBoundsException(msg)
/**
* Further read:
* - http://www.docjar.com/docs/api/sun/misc/Unsafe.html
*
* Created by minjaesong on 2019-06-21.
*/
internal object UnsafeHelper {
val unsafe: Unsafe
@@ -87,8 +89,8 @@ internal class UnsafePtr(pointer: Long, allocSize: Long) {
//// You may break the glass and use this tool when some fucking incomprehensible bugs ("vittujen vitun bugit")
//// appear (e.g. getting garbage values when it fucking shouldn't)
assert(!destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") }
if (index !in 0 until size) throw IndexOutOfBoundsException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
if (destroyed) { throw DanglingPointerException("The pointer is already destroyed ($this)") }
if (index !in 0 until size) throw AddressOverflowException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
}
operator fun get(index: Long): Byte {

View File

@@ -28,69 +28,27 @@ class AudioAdapter(val vm: VM) : PeriBase {
internal val sampleBin = UnsafeHelper.allocate(114687L)
internal val instruments = Array(256) { TaudInst() }
internal val playdata = Array(256) { Array(64) { TaudPlayData(0,0,0,0,0,0,0,0) } }
internal val playheads = Array(4) { Playhead() }
internal val playheads: Array<Playhead>
internal val cueSheet = Array(2048) { PlayCue() }
internal val pcmBin = UnsafeHelper.allocate(65536L)
private lateinit var audioDevices: Array<AudioDevice>
// private var audioDevices: Array<AudioDevice>
private val renderThreads = Array(4) { Thread(getRenderFun(it)) }
private val writeQueueingThreads = Array(4) { Thread(getQueueingFun(it)) }
// private val writeQueues = Array(4) { Queue<FloatArray>() }
/*private val alSources = Array(4) {
val audioField = OpenALAudioDevice::class.java.getDeclaredField("audio")
audioField.isAccessible = true
val audio = audioField.get(audioDevices[it]) as OpenALLwjgl3Audio
val obtainSourceMethod = OpenALLwjgl3Audio::class.java.getDeclaredMethod("obtainSource", java.lang.Boolean.TYPE)
obtainSourceMethod.isAccessible = true
val alSource = obtainSourceMethod.invoke(audio, true) as Int
alSource
}
private val alBuffers = Array(4) {
val buffers = IntArray(3)
AL11.alGenBuffers(buffers)
buffers
}
private fun freeAlSources() {
audioDevices.forEachIndexed { index, adev ->
val audioField = OpenALAudioDevice::class.java.getDeclaredField("audio")
audioField.isAccessible = true
val audio = audioField.get(adev) as OpenALLwjgl3Audio
val freeSourceMethod = OpenALLwjgl3Audio::class.java.getDeclaredMethod("freeSource", java.lang.Integer.TYPE)
freeSourceMethod.isAccessible = true
freeSourceMethod.invoke(audio, alSources[index])
}
}
private fun enqueuePacket(alSource: Int, alBuffer: Int, data: ByteBuffer) {
AL11.alBufferData(alBuffer, AL11.AL_FORMAT_STEREO8, data, SAMPLING_RATE)
AL11.alSourceQueueBuffers(alSource, alBuffer)
}*/
private val pcmCurrentPosInSamples = ShortArray(4)
private var pcmPlaybackWatchdogs = Array(4) { Thread {
} }
private fun getRenderFun(pheadNum: Int): () -> Unit = { while (true) {
render(playheads[pheadNum], pheadNum)
render(playheads[pheadNum])
Thread.sleep(1)
} }
private fun getQueueingFun(pheadNum: Int): () -> Unit = { while (true) {
playheads[pheadNum].let {
if (it.pcmUploadLength > 0) {
if (it.pcmQueue.size < 4 && it.pcmUpload && it.pcmUploadLength > 0) {
printdbg("Downloading samples ${it.pcmUploadLength}")
val samples = FloatArray(it.pcmUploadLength) { pcmBin[it.toLong()].toUint().div(255f) * 2f - 1f }
val samples = ByteArray(it.pcmUploadLength)
UnsafeHelper.memcpyRaw(null, pcmBin.ptr, samples, UnsafeHelper.getArrayOffset(samples), it.pcmUploadLength.toLong())
it.pcmQueue.addLast(samples)
it.pcmUploadLength = 0
@@ -115,14 +73,21 @@ class AudioAdapter(val vm: VM) : PeriBase {
printdbg("buffer size: $deviceBufferSize x $deviceBufferCount")
audioDevices = Array(4) { OpenALBufferedAudioDevice(
Gdx.audio as OpenALLwjgl3Audio,
SAMPLING_RATE,
false,
deviceBufferSize,
deviceBufferCount) {
playheads = Array(4) {
val adev = OpenALBufferedAudioDevice(
Gdx.audio as OpenALLwjgl3Audio,
SAMPLING_RATE,
false,
deviceBufferSize,
deviceBufferCount
) {
}
Playhead(index = it, audioDevice = adev)
}
} }
// printdbg("AudioAdapter latency: ${audioDevice.latency}")
@@ -134,7 +99,7 @@ class AudioAdapter(val vm: VM) : PeriBase {
/**
* Put this function into a separate thread and keep track of the delta time by yourself
*/
private fun render(playhead: Playhead, pheadNum: Int) {
private fun render(playhead: Playhead) {
if (playhead.isPcmMode) {
val writeQueue = playhead.pcmQueue
@@ -146,14 +111,12 @@ class AudioAdapter(val vm: VM) : PeriBase {
val samples = writeQueue.removeFirst()
playhead.position = writeQueue.size
printdbg("P${pheadNum+1} Vol ${playhead.masterVolume}; LpP ${playhead.pcmUploadLength}; start playback...")
// printdbg("P${playhead.index+1} Vol ${playhead.masterVolume}; LpP ${playhead.pcmUploadLength}; start playback...")
// printdbg(""+(0..42).joinToString { String.format("%.2f", samples[it]) })
if (playhead.masterVolume == 0) printdbg("P${pheadNum+1} volume is zero!")
audioDevices[pheadNum].setVolume(playhead.masterVolume / 255f)
audioDevices[pheadNum].writeSamples(samples, 0, samples.size)
playhead.audioDevice.writeSamplesUI8(samples, 0, samples.size)
printdbg("P${pheadNum+1} go back to spinning")
// printdbg("P${playhead.index+1} go back to spinning")
}
else if (playhead.isPlaying) {
@@ -215,8 +178,7 @@ class AudioAdapter(val vm: VM) : PeriBase {
override fun dispose() {
renderThreads.forEach { it.interrupt() }
writeQueueingThreads.forEach { it.interrupt() }
audioDevices.forEach { it.dispose() }
// freeAlSources()
playheads.forEach { it.dispose() }
sampleBin.destroy()
pcmBin.destroy()
}
@@ -270,6 +232,8 @@ class AudioAdapter(val vm: VM) : PeriBase {
internal object PlayInstNop : PlayInstruction(0)
internal data class Playhead(
val index: Int,
var position: Int = 0,
var pcmUploadLength: Int = 0,
var masterVolume: Int = 0,
@@ -277,11 +241,13 @@ class AudioAdapter(val vm: VM) : PeriBase {
// flags
var isPcmMode: Boolean = false,
var isPlaying: Boolean = false,
var samplingRateMult: ThreeFiveMiniUfloat = ThreeFiveMiniUfloat(32),
// var samplingRateMult: ThreeFiveMiniUfloat = ThreeFiveMiniUfloat(32),
var bpm: Int = 120, // "stored" as 96
var tickRate: Int = 6,
var pcmUpload: Boolean = false,
var pcmQueue: Queue<FloatArray> = Queue<FloatArray>()
var pcmQueue: Queue<ByteArray> = Queue<ByteArray>(),
val audioDevice: OpenALBufferedAudioDevice
) {
fun read(index: Int): Byte = when (index) {
0 -> position.toByte()
@@ -291,31 +257,37 @@ class AudioAdapter(val vm: VM) : PeriBase {
4 -> masterVolume.toByte()
5 -> masterPan.toByte()
6 -> (isPcmMode.toInt().shl(7) or isPlaying.toInt().shl(4)).toByte()
7 -> samplingRateMult.index.toByte()
7 -> 0
8 -> (bpm - 24).toByte()
9 -> tickRate.toByte()
else -> throw InternalError("Bad offset $index")
}
fun write(index: Int, byte: Int) = when (index) {
0 -> if (!isPcmMode) { position = position.and(0xff00) or position } else {}
1 -> if (!isPcmMode) { position = position.and(0x00ff) or position.shl(8) } else {}
2 -> { pcmUploadLength = pcmUploadLength.and(0xff00) or pcmUploadLength }
3 -> { pcmUploadLength = pcmUploadLength.and(0x00ff) or pcmUploadLength.shl(8) }
4 -> { masterVolume = byte }
5 -> { masterPan = byte }
6 -> { byte.let {
val oldPcmMode = isPcmMode
isPcmMode = (it and 0b10000000) != 0
isPlaying = (it and 0b00010000) != 0
fun write(index: Int, byte: Int) {
val byte = byte and 255
when (index) {
0 -> if (!isPcmMode) { position = position.and(0xff00) or position } else {}
1 -> if (!isPcmMode) { position = position.and(0x00ff) or position.shl(8) } else {}
2 -> { pcmUploadLength = pcmUploadLength.and(0xff00) or pcmUploadLength }
3 -> { pcmUploadLength = pcmUploadLength.and(0x00ff) or pcmUploadLength.shl(8) }
4 -> {
masterVolume = byte
audioDevice.setVolume(masterVolume / 255f)
}
5 -> { masterPan = byte }
6 -> { byte.let {
val oldPcmMode = isPcmMode
isPcmMode = (it and 0b10000000) != 0
isPlaying = (it and 0b00010000) != 0
if (it and 0b01000000 != 0 || oldPcmMode != isPcmMode) resetParams()
if (it and 0b00100000 != 0) purgeQueue()
} }
7 -> { samplingRateMult = ThreeFiveMiniUfloat(byte) }
8 -> { bpm = byte + 24 }
9 -> { tickRate = byte }
else -> throw InternalError("Bad offset $index")
if (it and 0b01000000 != 0 || oldPcmMode != isPcmMode) resetParams()
if (it and 0b00100000 != 0) purgeQueue()
} }
7 -> if (isPcmMode) { pcmUpload = true } else {}
8 -> { bpm = byte + 24 }
9 -> { tickRate = byte }
else -> throw InternalError("Bad offset $index")
}
}
fun getSamplingRate() = 30000 - ((bpm - 24).and(255) or tickRate.and(255).shl(8)).toShort().toInt()
@@ -338,6 +310,10 @@ class AudioAdapter(val vm: VM) : PeriBase {
pcmUploadLength = 0
}
}
fun dispose() {
audioDevice.dispose()
}
}
internal data class TaudPlayData(

View File

@@ -3,6 +3,8 @@ package net.torvald.tsvm.peripheral
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.InputProcessor
import net.torvald.AddressOverflowException
import net.torvald.DanglingPointerException
import net.torvald.UnsafeHelper
import net.torvald.tsvm.CircularArray
import net.torvald.tsvm.VM
@@ -159,77 +161,96 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
override fun mmio_write(addr: Long, byte: Byte) {
val adi = addr.toInt()
val bi = byte.toInt().and(255)
when (addr) {
37L -> keyboardBuffer.appendHead(byte)
38L -> {
keyboardInputRequested = (byte.isNonZero())
if (keyboardInputRequested) keyboardBuffer.clear()
try {
when (addr) {
37L -> keyboardBuffer.appendHead(byte)
38L -> {
keyboardInputRequested = (byte.isNonZero())
if (keyboardInputRequested) keyboardBuffer.clear()
}
39L -> rawInputFunctionLatched = (byte.isNonZero())
in 40..47 -> keyEventBuffers[adi - 40] = byte
68L -> {
uptimeCounterLatched = byte.and(0b01).isNonZero()
RTClatched = byte.and(0b10).isNonZero()
}
88L -> vm.romMapping = bi
89L -> {
acpiShutoff = byte.and(-128).isNonZero()
}
in 1024..2047 -> peripheralFast[addr - 1024] = byte
4076L -> blockTransferPorts[0].statusCode.set(bi)
4077L -> blockTransferPorts[1].statusCode.set(bi)
4078L -> blockTransferPorts[2].statusCode.set(bi)
4079L -> blockTransferPorts[3].statusCode.set(bi)
4084L ->
blockTransferPorts[0].blockSize.getAcquire().let {
blockTransferPorts[0].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
}
4085L -> {
blockTransferPorts[0].hasNext.set(byte < 0)
blockTransferPorts[0].blockSize.getAcquire().let {
blockTransferPorts[0].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
}
}
4086L -> blockTransferPorts[1].blockSize.getAcquire().let {
blockTransferPorts[1].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
}
4087L -> {
blockTransferPorts[1].hasNext.set(byte < 0)
blockTransferPorts[1].blockSize.getAcquire().let {
blockTransferPorts[1].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
}
}
4088L -> blockTransferPorts[2].blockSize.getAcquire().let {
blockTransferPorts[2].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
}
4089L -> {
blockTransferPorts[2].hasNext.set(byte < 0)
blockTransferPorts[2].blockSize.getAcquire().let {
blockTransferPorts[2].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
}
}
4090L -> blockTransferPorts[3].blockSize.getAcquire().let {
blockTransferPorts[3].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
}
4091L -> {
blockTransferPorts[3].hasNext.set(byte < 0)
blockTransferPorts[3].blockSize.getAcquire().let {
blockTransferPorts[3].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
}
}
in 4092..4095 -> setBlockTransferPortStatus(adi - 4092, byte)
in 4096..8191 -> blockTransferTx[0][addr - 4096] = byte
in 8192..12287 -> blockTransferTx[1][addr - 8192] = byte
in 12288..16383 -> blockTransferTx[2][addr - 12288] = byte
in 16384..20479 -> blockTransferTx[3][addr - 16384] = byte
in 131072..262143 -> vm.peripheralTable[1].peripheral?.mmio_write(addr - 131072, byte)
in 262144..393215 -> vm.peripheralTable[2].peripheral?.mmio_write(addr - 262144, byte)
in 393216..524287 -> vm.peripheralTable[3].peripheral?.mmio_write(addr - 393216, byte)
in 524288..655359 -> vm.peripheralTable[4].peripheral?.mmio_write(addr - 524288, byte)
in 655360..786431 -> vm.peripheralTable[5].peripheral?.mmio_write(addr - 655360, byte)
in 786432..917503 -> vm.peripheralTable[6].peripheral?.mmio_write(addr - 786432, byte)
in 917504..1048575 -> vm.peripheralTable[7].peripheral?.mmio_write(addr - 917504, byte)
}
39L -> rawInputFunctionLatched = (byte.isNonZero())
in 40..47 -> keyEventBuffers[adi - 40] = byte
68L -> {
uptimeCounterLatched = byte.and(0b01).isNonZero()
RTClatched = byte.and(0b10).isNonZero()
}
88L -> vm.romMapping = bi
89L -> { acpiShutoff = byte.and(-128).isNonZero() }
in 1024..2047 -> peripheralFast[addr - 1024] = byte
4076L -> blockTransferPorts[0].statusCode.set(bi)
4077L -> blockTransferPorts[1].statusCode.set(bi)
4078L -> blockTransferPorts[2].statusCode.set(bi)
4079L -> blockTransferPorts[3].statusCode.set(bi)
4084L ->
blockTransferPorts[0].blockSize.getAcquire().let {
blockTransferPorts[0].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
4085L -> {
blockTransferPorts[0].hasNext.set(byte < 0)
blockTransferPorts[0].blockSize.getAcquire().let {
blockTransferPorts[0].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
}
4086L -> blockTransferPorts[1].blockSize.getAcquire().let {
blockTransferPorts[1].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
4087L -> {
blockTransferPorts[1].hasNext.set(byte < 0)
blockTransferPorts[1].blockSize.getAcquire().let {
blockTransferPorts[1].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
}
4088L -> blockTransferPorts[2].blockSize.getAcquire().let {
blockTransferPorts[2].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
4089L -> {
blockTransferPorts[2].hasNext.set(byte < 0)
blockTransferPorts[2].blockSize.getAcquire().let {
blockTransferPorts[2].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
}
4090L -> blockTransferPorts[3].blockSize.getAcquire().let {
blockTransferPorts[3].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
4091L -> {
blockTransferPorts[3].hasNext.set(byte < 0)
blockTransferPorts[3].blockSize.getAcquire().let {
blockTransferPorts[3].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
}
in 4092..4095 -> setBlockTransferPortStatus(adi - 4092, byte)
in 4096..8191 -> blockTransferTx[0][addr - 4096] = byte
in 8192..12287 -> blockTransferTx[1][addr - 8192] = byte
in 12288..16383 -> blockTransferTx[2][addr - 12288] = byte
in 16384..20479 -> blockTransferTx[3][addr - 16384] = byte
in 131072..262143 -> vm.peripheralTable[1].peripheral?.mmio_write(addr - 131072, byte)
in 262144..393215 -> vm.peripheralTable[2].peripheral?.mmio_write(addr - 262144, byte)
in 393216..524287 -> vm.peripheralTable[3].peripheral?.mmio_write(addr - 393216, byte)
in 524288..655359 -> vm.peripheralTable[4].peripheral?.mmio_write(addr - 524288, byte)
in 655360..786431 -> vm.peripheralTable[5].peripheral?.mmio_write(addr - 655360, byte)
in 786432..917503 -> vm.peripheralTable[6].peripheral?.mmio_write(addr - 786432, byte)
in 917504..1048575 -> vm.peripheralTable[7].peripheral?.mmio_write(addr - 917504, byte)
}
catch (_: DanglingPointerException) {}
catch (_: AddressOverflowException) {}
}
override fun dispose() {

View File

@@ -5,6 +5,7 @@ import com.badlogic.gdx.backends.lwjgl3.audio.OpenALAudioDevice
import com.badlogic.gdx.backends.lwjgl3.audio.OpenALLwjgl3Audio
import com.badlogic.gdx.math.MathUtils
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL10
import org.lwjgl.openal.AL11
@@ -64,6 +65,20 @@ class OpenALBufferedAudioDevice(
tempBuffer = BufferUtils.createByteBuffer(bufferSize)
}
fun writeSamplesUI8(samples: ByteArray, offset: Int, numSamples: Int) {
if (bytes == null || bytes!!.size < numSamples * 2) bytes = ByteArray(numSamples * 2)
val end = Math.min(offset + numSamples, samples.size)
var i = offset
var ii = 0
while (i < end) {
val sample = ui8toI16Hi[samples[i].toUint()]
bytes!![ii++] = sample
bytes!![ii++] = sample
i++
}
writeSamples(bytes!!, 0, numSamples * 2)
}
override fun writeSamples(samples: ShortArray, offset: Int, numSamples: Int) {
if (bytes == null || bytes!!.size < numSamples * 2) bytes = ByteArray(numSamples * 2)
val end = Math.min(offset + numSamples, samples.size)
@@ -228,5 +243,7 @@ class OpenALBufferedAudioDevice(
companion object {
private const val bytesPerSample = 2
private val ui8toI16Hi = ByteArray(256) { (128 + it).toByte() }
}
}