From 99a9b7ab6baceb45c527b0bf99e05d5bda6298ce Mon Sep 17 00:00:00 2001 From: minjaesong Date: Wed, 11 Jan 2023 17:15:07 +0900 Subject: [PATCH] command.js:fix:errorlevel not returned; playwav:interactive mode --- assets/disk0/tvdos/bin/command.js | 40 +++-- assets/disk0/tvdos/bin/encodemov.js | 6 +- assets/disk0/tvdos/bin/playmov.js | 43 ++++- assets/disk0/tvdos/bin/playwav.js | 80 ++++++++- assets/disk0/tvdos/tuidev/zfm.js | 28 ++- assets/disk0/tvdos/us_dvorak.key | 256 ++++++++++++++++++++++++++++ 6 files changed, 411 insertions(+), 42 deletions(-) create mode 100644 assets/disk0/tvdos/us_dvorak.key diff --git a/assets/disk0/tvdos/bin/command.js b/assets/disk0/tvdos/bin/command.js index 20ee266..1f861ff 100644 --- a/assets/disk0/tvdos/bin/command.js +++ b/assets/disk0/tvdos/bin/command.js @@ -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) diff --git a/assets/disk0/tvdos/bin/encodemov.js b/assets/disk0/tvdos/bin/encodemov.js index d36bb68..b2a4c21 100644 --- a/assets/disk0/tvdos/bin/encodemov.js +++ b/assets/disk0/tvdos/bin/encodemov.js @@ -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: diff --git a/assets/disk0/tvdos/bin/playmov.js b/assets/disk0/tvdos/bin/playmov.js index ea4034d..e6ad451 100644 --- a/assets/disk0/tvdos/bin/playmov.js +++ b/assets/disk0/tvdos/bin/playmov.js @@ -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() diff --git a/assets/disk0/tvdos/bin/playwav.js b/assets/disk0/tvdos/bin/playwav.js index 104ccbd..0f9f447 100644 --- a/assets/disk0/tvdos/bin/playwav.js +++ b/assets/disk0/tvdos/bin/playwav.js @@ -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) } diff --git a/assets/disk0/tvdos/tuidev/zfm.js b/assets/disk0/tvdos/tuidev/zfm.js index c0415f3..33c33c4 100644 --- a/assets/disk0/tvdos/tuidev/zfm.js +++ b/assets/disk0/tvdos/tuidev/zfm.js @@ -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 + + } } diff --git a/assets/disk0/tvdos/us_dvorak.key b/assets/disk0/tvdos/us_dvorak.key new file mode 100644 index 0000000..6c69489 --- /dev/null +++ b/assets/disk0/tvdos/us_dvorak.key @@ -0,0 +1,256 @@ +[[""],[undefined], +[undefined], +[""], +[undefined], +[""], +[""], +["0",")"], +["1","!"], +["2","@"], +["3","#"], +["4","$"], +["5","%"], +["6","^"], +["7","&"], +["8","*"], +["9","("], +["*"], +["#"], +[""], +[""], +[""], +[""], +["
"], +[""], +[""], +[""], +[""], +[""], +["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"], +[""], +[""], +[""], +[""], +[""], +[" "], +[""], +[""], +[""], +["\n"], +["\x08"], +["`","~"], +["[","{"], +["]","}"], +["/","?"], +["=","+"], +["\\","|"], +["s","S"], +["-",'_'], +["z","Z"], +[""], +[""], +[""], +[""], +["+"], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +["TCH_CHARSET>"], +["<:A:>"], +["<:B:>"], +["<:C:>"], +["<:X:>"], +["<:Y:>"], +["<:Z:>"], +["<:L1:>"], +["<:R1:>"], +["<:L2:>"], +["<:R2:>"], +["<:TL:>"], +["<:TR:>"], +["<:START:>"], +["<:SELECT:>"], +["<:MODE:>"], +[""], +[""], +[undefined], +[undefined], +[""], +[""], +[undefined], +[undefined], +[undefined], +[""], +[""], +[undefined], +[""], +[""], +[undefined], +[undefined], +[undefined], +[undefined], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +[""], +["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:>"] +]