audio device changes

This commit is contained in:
minjaesong
2026-04-16 15:04:44 +09:00
parent 2ac084acd7
commit 6aa2542bb8
11 changed files with 153 additions and 59 deletions

View File

@@ -4,7 +4,7 @@ music.pread(samples, 65534)
audio.setPcmMode(0)
audio.setMasterVolume(0, 255)
audio.putPcmDataByPtr(samples, 65534, 0)
audio.putPcmDataByPtr(0, samples, 65534, 0)
audio.setLoopPoint(0, 65534)
audio.play(0)*/
@@ -127,7 +127,7 @@ while (sampleSize > 0) {
let readLength = (sampleSize < BLOCK_SIZE) ? sampleSize : BLOCK_SIZE
readBytes(readLength, decodePtr)
audio.putPcmDataByPtr(decodePtr, readLength, 0)
audio.putPcmDataByPtr(0, decodePtr, readLength, 0)
audio.setSampleUploadLength(0, readLength)
audio.startSampleUpload(0)

View File

@@ -0,0 +1,5 @@
/**
* Hopper is a package manager for TSVM
* Created by CuriousTorvald on 2026-04-16
*/

View File

@@ -326,7 +326,7 @@ while (!stopPlay && seqread.getReadCount() < FILE_LENGTH) {
// RAW PCM packets (decode on the fly)
else if (packetType == 0x1000 || packetType == 0x1001) {
let frame = seqread.readBytes(readLength)
audio.putPcmDataByPtr(frame, readLength, 0)
audio.putPcmDataByPtr(0, frame, readLength, 0)
audio.setSampleUploadLength(0, readLength)
audio.startSampleUpload(0)
sys.free(frame)

View File

@@ -162,7 +162,7 @@ while (!stopPlay && seqread.getReadCount() < FILE_SIZE && readLength > 0) {
seqread.readBytes(readLength, readPtr)
audio.putPcmDataByPtr(readPtr, readLength, 0)
audio.putPcmDataByPtr(0, readPtr, readLength, 0)
audio.setSampleUploadLength(0, readLength)
audio.startSampleUpload(0)

View File

@@ -18,6 +18,7 @@ const ADDRESSING_INTERNAL = 0x02
const SND_BASE_ADDR = audio.getBaseAddr()
const SND_MEM_ADDR = audio.getMemAddr()
const pcm = require("pcm")
const AUDIO_DEVICE = 3
const MP2_FRAME_SIZE = [144,216,252,288,360,432,504,576,720,864,1008,1152,1440,1728]
const TAV_TEMPORAL_LEVELS = 2
@@ -152,10 +153,10 @@ graphics.clearPixels4(0)
const gpuGraphicsMode = graphics.getGraphicsMode()
// Initialize audio
audio.resetParams(0)
audio.purgeQueue(0)
audio.setPcmMode(0)
audio.setMasterVolume(0, 255)
audio.resetParams(AUDIO_DEVICE)
audio.purgeQueue(AUDIO_DEVICE)
audio.setPcmMode(AUDIO_DEVICE)
audio.setMasterVolume(AUDIO_DEVICE, 255)
// set colour zero as half-opaque black
graphics.setPalette(0, 0, 0, 0, 7)
@@ -1152,10 +1153,10 @@ try {
else if (keyCode == 62) { // SPACE - pause/resume
paused = !paused
if (paused) {
audio.stop(0)
audio.stop(AUDIO_DEVICE)
serial.println(`Paused at frame ${frameCount}`)
} else {
audio.play(0)
audio.play(AUDIO_DEVICE)
serial.println(`Resumed`)
}
}
@@ -1176,10 +1177,10 @@ try {
baseTimecodeFrameCount = 0
currentTimecodeNs = 0
nextSubtitleEventIndex = 0 // Reset subtitle event processing
audio.purgeQueue(0)
audio.purgeQueue(AUDIO_DEVICE)
if (paused) {
audio.play(0)
audio.stop(0)
audio.play(AUDIO_DEVICE)
audio.stop(AUDIO_DEVICE)
}
skipped = true
}
@@ -1201,10 +1202,10 @@ try {
baseTimecodeFrameCount = 0
currentTimecodeNs = 0
nextSubtitleEventIndex = 0 // Reset subtitle event processing
audio.purgeQueue(0)
audio.purgeQueue(AUDIO_DEVICE)
if (paused) {
audio.play(0)
audio.stop(0)
audio.play(AUDIO_DEVICE)
audio.stop(AUDIO_DEVICE)
}
skipped = true
}
@@ -1232,10 +1233,10 @@ try {
break
}
}
audio.purgeQueue(0)
audio.purgeQueue(AUDIO_DEVICE)
if (paused) {
audio.play(0)
audio.stop(0)
audio.play(AUDIO_DEVICE)
audio.stop(AUDIO_DEVICE)
}
skipped = true
}
@@ -1271,10 +1272,10 @@ try {
break
}
}
audio.purgeQueue(0)
audio.purgeQueue(AUDIO_DEVICE)
if (paused) {
audio.play(0)
audio.stop(0)
audio.play(AUDIO_DEVICE)
audio.stop(AUDIO_DEVICE)
}
skipped = true
} else if (!seekTarget) {
@@ -1313,7 +1314,7 @@ try {
baseTimecodeFrameCount = 0
currentTimecodeNs = 0
nextSubtitleEventIndex = 0 // Reset subtitle event processing
audio.purgeQueue(0)
audio.purgeQueue(AUDIO_DEVICE)
currentFileIndex++
if (skipped) {
skipped = false
@@ -1737,7 +1738,7 @@ try {
seqread.readBytes(audioLen, SND_BASE_ADDR - 2368)
audio.mp2Decode()
audio.mp2UploadDecoded(0)
audio.mp2UploadDecoded(AUDIO_DEVICE)
}
else if (packetType === TAV_PACKET_AUDIO_TAD) {
@@ -1750,7 +1751,7 @@ try {
seqread.readBytes(payloadLen, SND_MEM_ADDR - 262144)
audio.tadDecode()
audio.tadUploadDecoded(0, sampleLen)
audio.tadUploadDecoded(AUDIO_DEVICE, sampleLen)
}
else if (packetType === TAV_PACKET_AUDIO_NATIVE) {
// PCM length must not exceed 65536 bytes!
@@ -1762,10 +1763,10 @@ try {
let pcmLen = gzip.decompFromTo(zstdPtr, zstdLen, pcmPtr) // <- segfaults!
if (pcmLen > 65536) throw Error(`PCM data too long -- got ${pcmLen} bytes`)
audio.putPcmDataByPtr(pcmPtr, pcmLen, 0)
audio.putPcmDataByPtr(AUDIO_DEVICE, pcmPtr, pcmLen, 0)
audio.setSampleUploadLength(0, pcmLen)
audio.startSampleUpload(0)
audio.setSampleUploadLength(AUDIO_DEVICE, pcmLen)
audio.startSampleUpload(AUDIO_DEVICE)
sys.free(zstdPtr)
sys.free(pcmPtr)
@@ -2049,7 +2050,7 @@ try {
// Fire audio on first frame
if (!audioFired) {
audio.play(0)
audio.play(AUDIO_DEVICE)
audioFired = true
}
@@ -2137,7 +2138,7 @@ try {
// Fire audio on first frame
if (!audioFired) {
audio.play(0)
audio.play(AUDIO_DEVICE)
audioFired = true
}
@@ -2173,8 +2174,8 @@ try {
sys.memcpy(predecodedPcmBuffer + predecodedPcmOffset, SND_BASE_ADDR, uploadSize)
// Set upload parameters and trigger upload to queue
audio.setSampleUploadLength(0, uploadSize)
audio.startSampleUpload(0)
audio.setSampleUploadLength(AUDIO_DEVICE, uploadSize)
audio.startSampleUpload(AUDIO_DEVICE)
predecodedPcmOffset += uploadSize
}
@@ -2458,8 +2459,8 @@ finally {
sys.poke(-1299460, 20)
sys.poke(-1299460, 21)
audio.stop(0)
audio.purgeQueue(0)
audio.stop(AUDIO_DEVICE)
audio.purgeQueue(AUDIO_DEVICE)
}
graphics.setPalette(0, 0, 0, 0, 0)

View File

@@ -289,7 +289,7 @@ while (!stopPlay && seqread.getReadCount() < FILE_SIZE - 8) {
let decodedSampleLength = decodeInfilePcm(readPtr, decodePtr, readLength)
printdbg(` decodedSampleLength: ${decodedSampleLength}`)
audio.putPcmDataByPtr(decodePtr, decodedSampleLength, 0)
audio.putPcmDataByPtr(0, decodePtr, decodedSampleLength, 0)
audio.setSampleUploadLength(0, decodedSampleLength)
audio.startSampleUpload(0)

View File

@@ -292,6 +292,24 @@ function makeNoise(buf, length, offset, freq, type, op, amp, pan) {
}
}
function makeAliasedTriangleNES(buf, length, offset, freq, duty, op, amp, pan) {
// NES APU triangle — quantised to the authentic 32-step, 4-bit (0..15) staircase.
// The 32-step sequence is: 15,14,...,1,0, 0,1,...,14,15 (descending then ascending).
// This mirrors the real NES triangle DAC which has 32 equal-height steps per period.
// duty parameter is accepted for API symmetry but ignored (NES triangle is always symmetric).
if (op == null) op = 'add'
if (amp == null) amp = 0.5
if (pan == null) pan = 0.0
mixInto(buf, length, offset, op, amp, pan, function(i) {
const t = offset + i / HW_SAMPLING_RATE
const phase = (t * freq) % 1
const step32 = Math.floor(phase * 32) | 0 // 0..31
// step 0..15: descend from 15 to 0; step 16..31: ascend from 0 to 15
const level = (step32 < 16) ? (15 - step32) : (step32 - 16)
return level / 7.5 - 1.0 // map 0..15 → -1..+1
})
}
// ── Send to audio hardware ──────────────────────────────────────────────────
function sendBuffer(buf, playhead, offsetSec, lengthSec, stagingPtr) {
@@ -322,8 +340,49 @@ function sendBuffer(buf, playhead, offsetSec, lengthSec, stagingPtr) {
sys.poke(stagingPtr + 2 * i + 1, readU8(buf, 1, cursor + i))
}
// Wait for room in the playback queue (mirrors playwav.js idiom)
while (audio.getPosition(playhead) > 2) sys.sleep(2)
audio.putPcmDataByPtr(stagingPtr, take * 2, 0)
// while (audio.getPosition(playhead) > 2) sys.sleep(2)
audio.putPcmDataByPtr(stagingPtr, take * 2, playhead)
audio.setSampleUploadLength(playhead, take * 2)
audio.startSampleUpload(playhead)
remaining -= take
cursor += take
}
if (ownsStaging) sys.free(stagingPtr)
}
// Lazily-allocated JS-side interleave scratch; shared across sendBufferFast calls.
let _sendFastScratch = null
function sendBufferFast(buf, playhead, offsetSec, lengthSec, stagingPtr) {
// Like sendBuffer but interleaves L/R via a JS Uint8Array + one sys.pokeBytes per chunk,
// instead of ~2n sys.poke calls. Requires a non-native (JS-backed) buffer.
// Falls back to sendBuffer for native buffers.
if (isNative(buf)) { sendBuffer(buf, playhead, offsetSec, lengthSec, stagingPtr); return }
const start = (offsetSec != null) ? secToSamples(offsetSec) : 0
const total = (lengthSec != null) ? secToSamples(lengthSec) : (buf.samples - start)
const MAX_CHUNK = 32768
const ownsStaging = (stagingPtr == null)
if (ownsStaging) stagingPtr = sys.malloc(Math.min(total, MAX_CHUNK) * 2)
const scratchNeeded = Math.min(total, MAX_CHUNK) * 2
if (_sendFastScratch == null || _sendFastScratch.length < scratchNeeded) {
_sendFastScratch = new Uint8Array(scratchNeeded)
}
let remaining = total
let cursor = start
while (remaining > 0) {
const take = Math.min(remaining, MAX_CHUNK)
const L = buf[0], R = buf[1], sc = _sendFastScratch
for (let i = 0; i < take; i++) {
sc[2 * i] = L[cursor + i]
sc[2 * i + 1] = R[cursor + i]
}
sys.pokeBytes(stagingPtr, sc.subarray(0, take * 2), take * 2)
// while (audio.getPosition(playhead) > 2) sys.sleep(2)
audio.putPcmDataByPtr(playhead, stagingPtr, take * 2, 0)
audio.setSampleUploadLength(playhead, take * 2)
audio.startSampleUpload(playhead)
remaining -= take
@@ -336,6 +395,6 @@ function sendBuffer(buf, playhead, offsetSec, lengthSec, stagingPtr) {
exports = {
HW_SAMPLING_RATE,
makeBuffer, makeBufferNative, freeBufferNative, clearBuffer,
makeSquare, makeTriangle, makeAliasedTriangle, makeNoise,
sendBuffer
makeSquare, makeTriangle, makeAliasedTriangle, makeAliasedTriangleNES, makeNoise,
sendBuffer, sendBufferFast
}