mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-11 23:34:04 +09:00
new visualiser for pcm
This commit is contained in:
@@ -1,196 +1,81 @@
|
||||
// usage: playpcm audiofile.pcm [/i]
|
||||
let fileeeee = files.open(_G.shell.resolvePathInput(exec_args[1]).full)
|
||||
let filename = fileeeee.fullPath
|
||||
function printdbg(s) { if (0) serial.println(s) }
|
||||
// playpcm — raw PCMu8 stereo player with the shared playgui visualiser.
|
||||
// Usage: playpcm <file.pcm> [-i]
|
||||
|
||||
const interactive = exec_args[2] && exec_args[2].toLowerCase() == "-i"
|
||||
const pcm = require("pcm")
|
||||
const FILE_SIZE = files.open(filename).size
|
||||
|
||||
|
||||
|
||||
function printComments() {
|
||||
for (const [key, value] of Object.entries(comments)) {
|
||||
printdbg(`${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
function GCD(a, b) {
|
||||
a = Math.abs(a)
|
||||
b = Math.abs(b)
|
||||
if (b > a) {var temp = a; a = b; b = temp}
|
||||
while (true) {
|
||||
if (b == 0) return a
|
||||
a %= b
|
||||
if (a == 0) return b
|
||||
b %= a
|
||||
}
|
||||
}
|
||||
|
||||
function LCM(a, b) {
|
||||
return (!a || !b) ? 0 : Math.abs((a * b) / GCD(a, b))
|
||||
}
|
||||
|
||||
|
||||
|
||||
//println("Reading...")
|
||||
//serial.println("!!! READING")
|
||||
const fileHandle = files.open(_G.shell.resolvePathInput(exec_args[1]).full)
|
||||
const filePath = fileHandle.fullPath
|
||||
|
||||
const interactive = exec_args[2] && exec_args[2].toLowerCase() === "-i"
|
||||
const pcm = require("pcm")
|
||||
const seqread = require("seqread")
|
||||
seqread.prepare(filename)
|
||||
|
||||
|
||||
|
||||
|
||||
const gui = interactive ? require("playgui") : null
|
||||
|
||||
const FILE_SIZE = files.open(filePath).size
|
||||
|
||||
let BLOCK_SIZE = 4096
|
||||
let INFILE_BLOCK_SIZE = BLOCK_SIZE
|
||||
const QUEUE_MAX = 8 // according to the spec
|
||||
const INFILE_BLOCK_SIZE = BLOCK_SIZE
|
||||
const QUEUE_MAX = 8
|
||||
|
||||
let nChannels = 2
|
||||
let samplingRate = pcm.HW_SAMPLING_RATE;
|
||||
let blockSize = 2;
|
||||
let bitsPerSample = 8;
|
||||
let byterate = 2*samplingRate;
|
||||
let comments = {};
|
||||
let readPtr = undefined
|
||||
let decodePtr = undefined
|
||||
const samplingRate = pcm.HW_SAMPLING_RATE
|
||||
const byterate = 2 * samplingRate
|
||||
|
||||
function bytesToSec(i) {
|
||||
return i / byterate
|
||||
}
|
||||
function secToReadable(n) {
|
||||
let mins = ''+((n/60)|0)
|
||||
let secs = ''+(n % 60)
|
||||
return `${mins.padStart(2,'0')}:${secs.padStart(2,'0')}`
|
||||
}
|
||||
|
||||
let stopPlay = false
|
||||
con.curs_set(0)
|
||||
let [__, CONSOLE_WIDTH] = con.getmaxyx()
|
||||
if (interactive) {
|
||||
let [cy, cx] = con.getyx()
|
||||
// file name
|
||||
con.mvaddch(cy, 1)
|
||||
con.prnch(0xC9);con.prnch(0xCD);con.prnch(0xB5)
|
||||
print(fileeeee.name)
|
||||
con.prnch(0xC6);con.prnch(0xCD)
|
||||
print("\x84205u".repeat(CONSOLE_WIDTH - 26 - fileeeee.name.length))
|
||||
con.prnch(0xB5)
|
||||
print("Hold Bksp to Exit")
|
||||
con.prnch(0xC6);con.prnch(0xCD);con.prnch(0xBB)
|
||||
|
||||
// L R pillar
|
||||
con.prnch(0xBA)
|
||||
con.mvaddch(cy+1, CONSOLE_WIDTH, 0xBA)
|
||||
|
||||
// media info
|
||||
let mediaInfoStr = `Raw PCM 512kbps`
|
||||
con.move(cy+2,1)
|
||||
con.prnch(0xC8)
|
||||
print("\x84205u".repeat(CONSOLE_WIDTH - 5 - mediaInfoStr.length))
|
||||
con.prnch(0xB5)
|
||||
print(mediaInfoStr)
|
||||
con.prnch(0xC6);con.prnch(0xCD);con.prnch(0xBC)
|
||||
|
||||
con.move(cy+1, 2)
|
||||
}
|
||||
let [cy, cx] = con.getyx()
|
||||
let paintWidth = CONSOLE_WIDTH - 20
|
||||
// read chunks loop
|
||||
readPtr = sys.malloc(BLOCK_SIZE * bitsPerSample / 8)
|
||||
decodePtr = sys.malloc(BLOCK_SIZE * pcm.HW_SAMPLING_RATE / samplingRate)
|
||||
function bytesToSec(i) { return i / byterate }
|
||||
|
||||
seqread.prepare(filePath)
|
||||
|
||||
const readPtr = sys.malloc(BLOCK_SIZE)
|
||||
audio.resetParams(0)
|
||||
audio.purgeQueue(0)
|
||||
audio.setPcmMode(0)
|
||||
audio.setMasterVolume(0, 255)
|
||||
|
||||
let readLength = 1
|
||||
|
||||
function printPlayBar() {
|
||||
if (interactive) {
|
||||
let currently = seqread.getReadCount()
|
||||
let total = FILE_SIZE
|
||||
|
||||
let currentlySec = Math.round(bytesToSec(currently))
|
||||
let totalSec = Math.round(bytesToSec(total))
|
||||
|
||||
con.move(cy, 3)
|
||||
print(' '.repeat(15))
|
||||
con.move(cy, 3)
|
||||
|
||||
print(`${secToReadable(currentlySec)} / ${secToReadable(totalSec)}`)
|
||||
|
||||
con.move(cy, 17)
|
||||
print(' ')
|
||||
let progressbar = '\x84196u'.repeat(paintWidth + 1)
|
||||
print(progressbar)
|
||||
|
||||
con.mvaddch(cy, 18 + Math.round(paintWidth * (currently / total)), 0xDB)
|
||||
}
|
||||
if (interactive) {
|
||||
gui.audioInit({
|
||||
title: `${fileHandle.name} Raw PCM 32kHz Stereo`,
|
||||
tag: "PCM"
|
||||
})
|
||||
}
|
||||
|
||||
let stopPlay = false
|
||||
let errorlevel = 0
|
||||
let readLength = 1
|
||||
try {
|
||||
while (!stopPlay && seqread.getReadCount() < FILE_SIZE && readLength > 0) {
|
||||
if (interactive) {
|
||||
sys.poke(-40, 1)
|
||||
if (sys.peek(-41) == 67) {
|
||||
stopPlay = true
|
||||
}
|
||||
}
|
||||
while (!stopPlay && seqread.getReadCount() < FILE_SIZE && readLength > 0) {
|
||||
if (interactive && gui.audioIsExitRequested()) { stopPlay = true; break }
|
||||
|
||||
const queueSize = audio.getPosition(0)
|
||||
if (queueSize <= 1) {
|
||||
for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) {
|
||||
const remainingBytes = FILE_SIZE - seqread.getReadCount()
|
||||
readLength = (remainingBytes < INFILE_BLOCK_SIZE) ? remainingBytes : INFILE_BLOCK_SIZE
|
||||
if (readLength <= 0) break
|
||||
|
||||
let queueSize = audio.getPosition(0)
|
||||
if (queueSize <= 1) {
|
||||
seqread.readBytes(readLength, readPtr)
|
||||
|
||||
printPlayBar()
|
||||
// Raw PCMu8 stereo — sampleCount = bytes / 2.
|
||||
if (interactive) gui.audioFeedPcm(readPtr, readLength >> 1)
|
||||
|
||||
// upload four samples for lag-safely
|
||||
for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) {
|
||||
let remainingBytes = FILE_SIZE - seqread.getReadCount()
|
||||
audio.putPcmDataByPtr(0, readPtr, readLength, 0)
|
||||
audio.setSampleUploadLength(0, readLength)
|
||||
audio.startSampleUpload(0)
|
||||
|
||||
readLength = (remainingBytes < INFILE_BLOCK_SIZE) ? remainingBytes : INFILE_BLOCK_SIZE
|
||||
if (readLength <= 0) {
|
||||
printdbg(`readLength = ${readLength}`)
|
||||
break
|
||||
if (repeat > 1) sys.sleep(10)
|
||||
}
|
||||
|
||||
printdbg(`offset: ${seqread.getReadCount()}/${FILE_SIZE}; readLength: ${readLength}`)
|
||||
|
||||
seqread.readBytes(readLength, readPtr)
|
||||
|
||||
audio.putPcmDataByPtr(0, readPtr, readLength, 0)
|
||||
audio.setSampleUploadLength(0, readLength)
|
||||
audio.startSampleUpload(0)
|
||||
|
||||
|
||||
if (repeat > 1) sys.sleep(10)
|
||||
|
||||
printPlayBar()
|
||||
audio.play(0)
|
||||
}
|
||||
|
||||
audio.play(0)
|
||||
if (interactive) {
|
||||
const cur = seqread.getReadCount()
|
||||
gui.audioSetProgress(cur / FILE_SIZE, bytesToSec(cur), bytesToSec(FILE_SIZE))
|
||||
gui.audioRender()
|
||||
}
|
||||
sys.sleep(10)
|
||||
}
|
||||
|
||||
let remainingBytes = FILE_SIZE - seqread.getReadCount()
|
||||
printdbg(`readLength = ${readLength}; remainingBytes2 = ${remainingBytes}; seqread.getReadCount() = ${seqread.getReadCount()};`)
|
||||
|
||||
|
||||
sys.sleep(10)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
printerrln(e)
|
||||
errorlevel = 1
|
||||
}
|
||||
finally {
|
||||
//audio.stop(0)
|
||||
} finally {
|
||||
if (readPtr !== undefined) sys.free(readPtr)
|
||||
if (decodePtr !== undefined) sys.free(decodePtr)
|
||||
if (interactive) gui.audioClose()
|
||||
}
|
||||
|
||||
return errorlevel
|
||||
|
||||
|
||||
Reference in New Issue
Block a user