command.js:fix:errorlevel not returned; playwav:interactive mode

This commit is contained in:
minjaesong
2023-01-11 17:15:07 +09:00
parent abcf947ff3
commit 99a9b7ab6b
6 changed files with 411 additions and 42 deletions

View File

@@ -5,7 +5,7 @@ let shell_pwd = [""]
let goInteractive = false
let goFancy = false
let DEBUG_PRINT = false
let DEBUG_PRINT = true
let errorlevel = 0
@@ -318,29 +318,29 @@ shell.resolvePathInput = function(input) {
}
debugprintln("command.js > resolvePathInput > sanitised input: "+pathstr)
// debugprintln("command.js > resolvePathInput > sanitised input: "+pathstr)
let startsWithSlash = pathstr.startsWith('/')
let newPwd = []
debugprintln("command.js > resolvePathInput > path starts with slash: "+startsWithSlash)
// debugprintln("command.js > resolvePathInput > path starts with slash: "+startsWithSlash)
// split them into an array while filtering empty elements except for the root 'head'
let ipwd = (startsWithSlash ? [""] : shell_pwd).concat(pathstr.split("/").filter(function(it) { return (it.length > 0); }))
debugprintln("command.js > resolvePathInput > ipwd = "+ipwd)
debugprintln("command.js > resolvePathInput > newPwd = "+newPwd)
// debugprintln("command.js > resolvePathInput > ipwd = "+ipwd)
// debugprintln("command.js > resolvePathInput > newPwd = "+newPwd)
// process dots
ipwd.forEach(function(it) {
debugprintln("command.js > resolvePathInput > ipwd.forEach > it = "+it)
// debugprintln("command.js > resolvePathInput > ipwd.forEach > it = "+it)
if (it === ".." && newPwd[1] !== undefined) {
newPwd.pop()
}
else if (it !== ".." && it !== ".") {
newPwd.push(it)
}
debugprintln("command.js > resolvePathInput > newPwd = "+newPwd)
// debugprintln("command.js > resolvePathInput > newPwd = "+newPwd)
})
// construct new pathstr from pwd arr so it will be sanitised
@@ -631,6 +631,14 @@ shell.execute = function(line) {
let retValue = undefined // return value of the previous statement
for (let c = 0; c < statements.length; c++) {
let tokens = statements[c]
if (retValue) {
debugprintln(`[shell.execute] previous statement "${tokens.join(' ')}" had non-zero errorlevel: ${retValue}, raising error...`)
return retValue
}
let op = operators[c]
// TODO : if operator is not undefined, swap built-in print functions with ones that 'prints' on pipes instead of stdout
@@ -653,8 +661,6 @@ shell.execute = function(line) {
let tokens = statements[c]
let cmd = tokens[0]
if (cmd === undefined || cmd === '') {
retValue = 0
@@ -694,9 +700,7 @@ shell.execute = function(line) {
let search = searchDir[i]; if (!search.endsWith('\\')) search += '\\'
searchPath = trimStartRevSlash(search + cmd + pathExt[j])
if (DEBUG_PRINT) {
debugprintln("[command.js > shell.execute] file search path: "+searchPath)
}
// debugprintln("[shell.execute] file search path: "+searchPath)
searchFile = files.open(`${CURRENT_DRIVE}:\\${searchPath}`)
if (searchFile.exists) {
@@ -736,6 +740,7 @@ shell.execute = function(line) {
sendLcdMsg(_G.shellProgramTitles[_G.shellProgramTitles.length - 1])
//serial.println(_G.shellProgramTitles)
debugprintln("[shell.execute] exec app " + searchFile.fullPath)
errorlevel = execApp(programCode, tokens, `tvdosExec$${cmd}$${searchPath}`.replaceAll(/[^A-Za-z0-9_]/g, "$")) // return value of undefined will cast into 0
}
catch (e) {
@@ -754,11 +759,13 @@ shell.execute = function(line) {
errorlevel = 1
}
finally {
debugprintln("[shell.execute] exec app " + searchFile.fullPath + "exit with no exception; errorlevel = " + errorlevel)
// sometimes no-error program may return nothing as the errorlevel; force set to 0 then.
if (!gotError && (errorlevel == undefined || (typeof errorlevel.trim == "function" && errorlevel.trim().length == 0) || isNaN(errorlevel)))
errorlevel = 0
debugprintln(`errorlevel: ${errorlevel}`)
debugprintln(`[shell.execute] errorlevel: ${errorlevel}`)
_G.shellProgramTitles.pop()
sendLcdMsg(_G.shellProgramTitles[_G.shellProgramTitles.length - 1])
@@ -777,10 +784,9 @@ shell.execute = function(line) {
debugprintln(`Statement #${c+1}: destroying pipe`)
debugprintln(`its content was: ${shell.removePipe()}`)
}
return retValue
}
serial.println("[shell.execute] final retvalue: "+retValue)
return retValue
}
shell.pipes = {} // syntax: _G.shell.pipes[name] = contents; all pipes are named pipes just like in Windows
shell.currentlyActivePipes = [] // Queue of pipe's names. Use shell.removePipe() to dequeue and shell.pushPipe() to enqueue.
@@ -887,7 +893,7 @@ if (goInteractive) {
else if (key === 10 || key === con.KEY_RETURN) {
println()
shell.execute(cmdbuf)
errorlevel = shell.execute(cmdbuf)
if (cmdbuf.trim().length > 0)
cmdHistory.push(cmdbuf)

View File

@@ -1,12 +1,12 @@
// some manual configurations
//
let IPFMODE = 2 // 1 or 2
let TOTAL_FRAMES = 3813
let TOTAL_FRAMES = 1318
let FPS = 30
let WIDTH = 560
let HEIGHT = 448
let PATHFUN = (i) => `/ddol2/${(''+i).padStart(5,'0')}.bmp` // how can be the image file found, if a frame number (starts from 1) were given
let AUDIOTRACK = 'ddol.pcm'
let PATHFUN = (i) => `/namu2/${(''+i).padStart(5,'0')}.png` // how can be the image file found, if a frame number (starts from 1) were given
let AUDIOTRACK = 'namu.u8'
// to export video to its frames:
// ffmpeg -i file.mp4 file/%05d.bmp
// the input frames must be resized (and cropped) beforehand, using ImageMagick is recommended, like so:

View File

@@ -1,4 +1,5 @@
// usage: playmov moviefile.mov [/i]
const interactive = exec_args[2].toLowerCase() == "/i"
const WIDTH = 560
const HEIGHT = 448
const FBUF_SIZE = WIDTH * HEIGHT
@@ -80,22 +81,43 @@ function getRGBfromScr(x, y) {
}
let oldBgcol = [0.0, 0.0, 0.0]
let stopPlay = false
if (interactive) {
con.move(1,1)
println("Push and hold Backspace to exit")
}
let notifHideTimer = 0
const NOTIF_SHOWUPTIME = 2000000000
let [cy, cx] = con.getyx()
let t1 = sys.nanoTime()
renderLoop:
while (seqread.getReadCount() < FILE_LENGTH) {
let t1 = sys.nanoTime()
while (!stopPlay && seqread.getReadCount() < FILE_LENGTH) {
if (akku >= frameTime) {
let frameUnit = 0 // 0: no decode, 1: normal playback, 2+: skip (n-1) frames
while (akku >= frameTime) {
while (!stopPlay && akku >= frameTime) {
if (interactive) {
sys.poke(-40, 1)
if (sys.peek(-41) == 67) {
stopPlay = true
}
}
akku -= frameTime
frameUnit += 1
}
if (frameUnit != 0) {
// skip frames if necessary
while (frameUnit >= 1 && seqread.getReadCount() < FILE_LENGTH) {
while (!stopPlay && frameUnit >= 1 && seqread.getReadCount() < FILE_LENGTH) {
if (interactive) {
sys.poke(-40, 1)
if (sys.peek(-41) == 67) {
stopPlay = true
}
}
let packetType = seqread.readShort()
@@ -212,6 +234,15 @@ while (seqread.getReadCount() < FILE_LENGTH) {
let t2 = sys.nanoTime()
akku += (t2 - t1) / 1000000000.0
if (interactive) {
notifHideTimer += (t2 - t1)
if (notifHideTimer > NOTIF_SHOWUPTIME) {
con.clear()
}
}
t1 = t2
}
let endTime = sys.nanoTime()

View File

@@ -1,8 +1,8 @@
// this program will serve as a step towards the ADPCM decoding, and tests if RIFF data are successfully decoded.
// usage: playwav audiofile.wav [/i]
let filename = _G.shell.resolvePathInput(exec_args[1]).full
function printdbg(s) { if (0) serial.println(s) }
const interactive = exec_args[2].toLowerCase() == "/i"
const seqread = require("seqread")
const pcm = require("pcm")
@@ -60,12 +60,27 @@ let nChannels;
let samplingRate;
let blockSize;
let bitsPerSample;
let byterate;
let comments = {};
let adpcmSamplesPerBlock;
let readPtr = undefined
let decodePtr = undefined
function bytesToSec(i) {
if (adpcmSamplesPerBlock) {
let newByteRate = samplingRate
let generatedSamples = i / blockSize * adpcmSamplesPerBlock
return generatedSamples / newByteRate
}
else {
return i / byterate
}
}
function secToReadable(n) {
let mins = ''+((n/60)|0)
let secs = ''+(n % 60)
return `${mins.padStart(2,'0')}:${secs.padStart(2,'0')}`
}
function checkIfPlayable() {
if (pcmType != 1 && pcmType != 2) return `PCM Type not LPCM/ADPCM (${pcmType})`
if (nChannels < 1 || nChannels > 2) return `Audio not mono/stereo but instead has ${nChannels} channels`
@@ -82,8 +97,15 @@ function decodeInfilePcm(inPtr, outPtr, inputLen) {
else
throw Error(`PCM Type not LPCM or ADPCM (${pcmType})`)
}
let stopPlay = false
con.curs_set(0)
if (interactive) {
println("Push and hold Backspace to exit")
}
let [cy, cx] = con.getyx()
let [__, CONSOLE_WIDTH] = con.getmaxyx()
// read chunks loop
while (seqread.getReadCount() < FILE_SIZE - 8) {
while (!stopPlay && seqread.getReadCount() < FILE_SIZE - 8) {
let chunkName = seqread.readFourCC()
let chunkSize = seqread.readInt()
printdbg(`Reading '${chunkName}' at ${seqread.getReadCount() - 8}`)
@@ -93,10 +115,17 @@ while (seqread.getReadCount() < FILE_SIZE - 8) {
pcmType = seqread.readShort()
nChannels = seqread.readShort()
samplingRate = seqread.readInt()
seqread.skip(4)
byterate = seqread.readInt()
blockSize = seqread.readShort()
bitsPerSample = seqread.readShort()
seqread.skip(chunkSize - 16)
if (pcmType != 2) {
seqread.skip(chunkSize - 16)
}
else {
seqread.skip(2)
adpcmSamplesPerBlock = seqread.readShort()
seqread.skip(chunkSize - (16 + 4))
}
// define BLOCK_SIZE as integer multiple of blockSize, for LPCM
// ADPCM will be decoded per-block basis
@@ -155,9 +184,41 @@ while (seqread.getReadCount() < FILE_SIZE - 8) {
audio.setMasterVolume(0, 255)
let readLength = 1
while (seqread.getReadCount() < startOffset + chunkSize && readLength > 0) {
while (!stopPlay && seqread.getReadCount() < startOffset + chunkSize && readLength > 0) {
if (interactive) {
sys.poke(-40, 1)
if (sys.peek(-41) == 67) {
stopPlay = true
}
}
let queueSize = audio.getPosition(0)
if (queueSize <= 1) {
if (interactive) {
let currently = seqread.getReadCount() - startOffset
let total = FILE_SIZE - startOffset - 8
let currentlySec = Math.round(bytesToSec(currently))
let totalSec = Math.round(bytesToSec(total))
con.move(cy, 1)
print(' '.repeat(40))
con.move(cy, 1)
print(`${secToReadable(currentlySec)} / ${secToReadable(totalSec)}`)
con.move(cy, 15)
print(' ')
let paintWidth = CONSOLE_WIDTH - 16
let progressbar = '\x84205u'.repeat(paintWidth + 1)
print(progressbar)
con.mvaddch(cy, 16 + Math.round(paintWidth * (currently / total)), 0xDB)
}
// upload four samples for lag-safely
for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) {
let remainingBytes = FILE_SIZE - 8 - seqread.getReadCount()
@@ -179,6 +240,7 @@ while (seqread.getReadCount() < FILE_SIZE - 8) {
audio.setSampleUploadLength(0, decodedSampleLength)
audio.startSampleUpload(0)
if (repeat > 1) sys.sleep(10)
}
@@ -187,7 +249,7 @@ while (seqread.getReadCount() < FILE_SIZE - 8) {
let remainingBytes = FILE_SIZE - 8 - seqread.getReadCount()
printdbg(`readLength = ${readLength}; remainingBytes2 = ${remainingBytes}; seqread.getReadCount() = ${seqread.getReadCount()}; startOffset + chunkSize = ${startOffset + chunkSize}`)
sys.spin()
sys.sleep(10)
}

View File

@@ -31,10 +31,10 @@ const COL_HL_EXT = {
}
const EXEC_FUNS = {
"wav": (f) => { _G.shell.execute(`playwav ${f}`) },
"adpcm": (f) => { _G.shell.execute(`playwav ${f}`) },
"mov": (f) => { _G.shell.execute(`playmov ${f}`) },
"bas": (f) => { _G.shell.execute(`basic ${f}`) }
"wav": (f) => _G.shell.execute(`playwav ${f} /i`),
"adpcm": (f) => _G.shell.execute(`playwav ${f} /i`),
"mov": (f) => _G.shell.execute(`playmov ${f} /i`),
"bas": (f) => _G.shell.execute(`basic ${f}`)
}
let windowMode = 0 // 0 == left, 1 == right
@@ -345,17 +345,27 @@ while (!exit) {
}
else {
let fileext = selectedFile.name.substring(selectedFile.name.lastIndexOf(".") + 1).toLowerCase()
let execfun = EXEC_FUNS[fileext] || ((f) => { _G.shell.execute(f) })
let execfun = EXEC_FUNS[fileext] || ((f) => _G.shell.execute(f))
let errorlevel = 0
con.curs_set(1);clearScr();con.move(1,1)
try {
serial.println(selectedFile.fullPath)
execfun(selectedFile.fullPath)
errorlevel = execfun(selectedFile.fullPath)
// serial.println("1 errorlevel = " + errorlevel)
}
catch (e) {
// TODO popup error
serial.println(e)
println(e)
errorlevel = 1
// serial.println("2 errorlevel = " + errorlevel)
}
if (errorlevel) {
println("Hit Return/Enter key to continue . . . .")
sys.read()
}
con.curs_set(0);clearScr()
redraw()
}
@@ -366,6 +376,10 @@ while (!exit) {
cursor[windowMode] = 0; scroll[windowMode] = 0
drawFilePanel()
}
else {
// TODO list of drives
}
}

View File

@@ -0,0 +1,256 @@
[[""],[undefined],
[undefined],
["<HOME>"],
[undefined],
["<CALL>"],
["<ENDCALL>"],
["0",")"],
["1","!"],
["2","@"],
["3","#"],
["4","$"],
["5","%"],
["6","^"],
["7","&"],
["8","*"],
["9","("],
["*"],
["#"],
["<UP>"],
["<DOWN>"],
["<LEFT>"],
["<RIGHT>"],
["<CENTER>"],
["<VOL_UP>"],
["<VOL_DOWN>"],
["<POWER>"],
["<CAMERA>"],
["<CLEAR>"],
["a","A"],
["x","X"],
["j","J"],
["e","E"],
[".",">"],
["u","U"],
["i","I"],
["d","D"],
["c","C"],
["h","H"],
["t","T"],
["n","N"],
["m","M"],
["b","B"],
["r","R"],
["l","L"],
["'",'"'],
["p","P"],
["o","O"],
["y","Y"],
["g","G"],
["k","K"],
[",","<"],
["q","Q"],
["f","F"],
[";",":"],
["w","W"],
["v","V"],
["<ALT_L>"],
["<ALT_R>"],
["<SHIFT_L>"],
["<SHIFT_R>"],
["<TAB>"],
[" "],
["<SYM>"],
["<EXPLORER>"],
["<ENVELOPE>"],
["\n"],
["\x08"],
["`","~"],
["[","{"],
["]","}"],
["/","?"],
["=","+"],
["\\","|"],
["s","S"],
["-",'_'],
["z","Z"],
["<AT>"],
["<NUM_LOCK>"],
["<HEADSETHOOK>"],
["<FOCUS>"],
["+"],
["<MENU>"],
["<NOTIFICATION>"],
["<SEARCH>"],
["<PLAY_PAUSE>"],
["<STOP>"],
["<NEXT>"],
["<PREV>"],
["<REW>"],
["<FFWD>"],
["<MUTE>"],
["<PAGE_UP>"],
["<PAGE_DOWN>"],
["<PICTSYMBOLS>"],
["<SW:>TCH_CHARSET>"],
["<:A:>"],
["<:B:>"],
["<:C:>"],
["<:X:>"],
["<:Y:>"],
["<:Z:>"],
["<:L1:>"],
["<:R1:>"],
["<:L2:>"],
["<:R2:>"],
["<:TL:>"],
["<:TR:>"],
["<:START:>"],
["<:SELECT:>"],
["<:MODE:>"],
["<ESC>"],
["<DEL>"],
[undefined],
[undefined],
["<CAPS_LOCK>"],
["<SCROLL_LOCK>"],
[undefined],
[undefined],
[undefined],
["<PRINT_SCREEN_SYS_RQ>"],
["<PAUSE_BREAK>"],
[undefined],
["<END>"],
["<INSERT>"],
[undefined],
[undefined],
[undefined],
[undefined],
["<CTRL_L>"],
["<CTRL_R>"],
["<F1>"],
["<F2>"],
["<F3>"],
["<F4>"],
["<F5>"],
["<F6>"],
["<F7>"],
["<F8>"],
["<F9>"],
["<F10>"],
["<F11>"],
["<F12>"],
["<NUM_LOCK>"],
["0"],
["1"],
["2"],
["3"],
["4"],
["5"],
["6"],
["7"],
["8"],
["9"],
["/"],
["*"],
["-"],
["+"],
["."],
["."],
["\n"],
["="],
["("],
[")"],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
[undefined],
["<:CIRCLE:>"]
]