Files
tsvm/assets/disk0/home/mp2test.js
2023-01-15 03:15:25 +09:00

215 lines
5.2 KiB
JavaScript

exec_args[1] = "A:/loopey.mp2"
const mp2 = require('mp2dec')
const pcm = require("pcm")
const interactive = exec_args[2] && exec_args[2].toLowerCase() == "/i"
function printdbg(s) { if (1) serial.println(s) }
class SequentialFileBuffer {
constructor(path, offset, length) {
if (Array.isArray(path)) throw Error("arg #1 is path(string), not array")
this.path = path
this.file = files.open(path)
this.offset = offset || 0
this.originalOffset = offset
this.length = length || this.file.size
this.seq = require("seqread")
this.seq.prepare(path)
}
readBytes(size, ptr) {
return this.seq.readBytes(size, ptr)
}
/*readFull(n) {
throw Error()
let ptr = this.seq.readBytes(n)
return ptr
}*/
readStr(n) {
let ptr = this.seq.readBytes(n)
let s = ''
for (let i = 0; i < n; i++) {
if (i >= this.length) break
s += String.fromCharCode(sys.peek(ptr + i))
}
sys.free(ptr)
return s
}
/*readByteNumbers(n) {
let ptr = this.seq.readBytes(n)
try {
let s = []
for (let i = 0; i < n; i++) {
if (i >= this.length) break
s.push(sys.peek(ptr + i))
}
sys.free(ptr)
return s
}
catch (e) {
println(`n: ${n}; ptr: ${ptr}`)
println(e)
}
}*/
unread(diff) {
let newSkipLen = this.seq.getReadCount() - diff
this.seq.prepare(this.path)
this.seq.skip(newSkipLen)
}
rewind() {
this.seq.prepare(this.path)
}
seek(p) {
this.seq.prepare(this.path)
this.seq.skip(p)
}
get byteLength() {
return this.length
}
/*get remaining() {
return this.length - this.getReadCount()
}*/
}
// this reads file, initialises all the craps, gets initial frame size, then discards everything; truly wasteful :)
// TODO use virtual audio hardware!
function getInitialFrameSize() {
let frame = filebuf.readBytes(4096)
let mp2 = require('mp2dec')
let mp2context = mp2.kjmp2_make_mp2_state()
mp2.kjmp2_init(mp2context)
let sampleRate = mp2.kjmp2_get_sample_rate(frame)
let [frameSize, _] = mp2.kjmp2_decode_frame(mp2context, frame, null, [])
filebuf.rewind()
sys.free(frame)
return [frameSize, sampleRate]
}
let filebuf = new SequentialFileBuffer(_G.shell.resolvePathInput(exec_args[1]).full)
const FILE_SIZE = filebuf.length - 100
const [FRAME_SIZE, SAMPLE_RATE] = getInitialFrameSize()
let bytes_left = FILE_SIZE
let decodedLength = 0
println(`Sampling rate: ${SAMPLE_RATE}, Frame size: ${FRAME_SIZE}`)
function decodeAndResample(inPtrL, inPtrR, outPtr, inputLen) {
// TODO resample
for (let k = 0; k < inputLen; k+=2) {
let sample = [
pcm.u16Tos16(sys.peek(inPtrL + k + 0) | (sys.peek(inPtrL + k + 1) << 8)),
pcm.u16Tos16(sys.peek(inPtrR + k + 0) | (sys.peek(inPtrR + k + 1) << 8))
]
sys.poke(outPtr + k, pcm.s16Tou8(sample[0]))
sys.poke(outPtr + k + 1, pcm.s16Tou8(sample[1]))
}
}
function decodeEvent(frameSize, len) {
if (interactive) {
sys.poke(-40, 1)
if (sys.peek(-41) == 67) {
stopPlay = true
throw "STOP"
}
}
// printPlayBar(pos)
let t2 = sys.nanoTime()
decodedLength += frameSize
printdbg(`Audio queue size: ${audio.getPosition(0)}/${QUEUE_MAX}`)
if (audio.getPosition(0) >= QUEUE_MAX) {
while (audio.getPosition(0) >= (QUEUE_MAX >>> 1)) {
printdbg(`Queue full, waiting until the queue has some space (${audio.getPosition(0)}/${QUEUE_MAX})`)
sys.sleep(bufRealTimeLen)
}
}
decodeAndResample(samplePtrL, samplePtrR, decodePtr, len)
audio.putPcmDataByPtr(decodePtr, len, 0)
audio.setSampleUploadLength(0, len)
audio.startSampleUpload(0)
sys.sleep(10)
let decodingTime = (t2 - t1) / 1000000.0
bufRealTimeLen = (len) / 64000.0 * 1000
t1 = t2
println(`Decoded ${decodedLength} bytes; target: ${bufRealTimeLen} ms, lag: ${decodingTime - bufRealTimeLen} ms`)
}
audio.resetParams(0)
audio.purgeQueue(0)
audio.setPcmMode(0)
audio.setPcmQueueCapacityIndex(0, 2) // queue size is now 8
const QUEUE_MAX = audio.getPcmQueueCapacity(0)
audio.setMasterVolume(0, 255)
audio.play(0)
let mp2context = audio.createNewMP2context()
audio.kjmp2_init(mp2context)
// decode frame
let frame = sys.malloc(FRAME_SIZE)
let samplePtrL = sys.malloc(6000) // 16b samples
let samplePtrR = sys.malloc(6000) // 16b samples
let decodePtr = sys.malloc(6000) // 8b samples
let t1 = sys.nanoTime()
let bufRealTimeLen = 36
while (bytes_left >= 0) {
// println(`Bytes left: ${bytes_left}`)
filebuf.readBytes(FRAME_SIZE, frame)
bytes_left -= FRAME_SIZE
let decodedL = []
let decodedR = []
let [frameSize, samples] = audio.kjmp2_decode_frame(mp2context, frame, true, samplePtrL, samplePtrR)
if (frameSize) {
// play using decodedLR
decodeEvent(frameSize, samples)
}
}
sys.free(frame)
sys.free(decodePtr)
sys.free(samplePtrL)
sys.free(samplePtrR)