From da5833935f33053cd202da139d91a3c25d73fef7 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 12 Jan 2023 19:42:12 +0900 Subject: [PATCH] more zfm stuffs --- assets/disk0/tvdos/bin/command.js | 2 +- assets/disk0/tvdos/bin/decodeipf.js | 36 +++++- assets/disk0/tvdos/bin/playmov.js | 6 +- assets/disk0/tvdos/bin/playpcm.js | 162 ++++++++++++++++++++++++ assets/disk0/tvdos/bin/playwav.js | 50 ++++---- assets/disk0/tvdos/include/pcm.js | 2 +- assets/disk0/tvdos/installer/install.js | 118 +++++++++++++---- assets/disk0/tvdos/tuidev/zfm.js | 3 + 8 files changed, 322 insertions(+), 57 deletions(-) create mode 100644 assets/disk0/tvdos/bin/playpcm.js diff --git a/assets/disk0/tvdos/bin/command.js b/assets/disk0/tvdos/bin/command.js index c6a12f6..dea0f9f 100644 --- a/assets/disk0/tvdos/bin/command.js +++ b/assets/disk0/tvdos/bin/command.js @@ -759,7 +759,7 @@ shell.execute = function(line) { errorlevel = 1 } finally { - debugprintln("[shell.execute] exec app " + searchFile.fullPath + "exit with no exception; errorlevel = " + errorlevel) + 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))) diff --git a/assets/disk0/tvdos/bin/decodeipf.js b/assets/disk0/tvdos/bin/decodeipf.js index 129fa93..880fe23 100644 --- a/assets/disk0/tvdos/bin/decodeipf.js +++ b/assets/disk0/tvdos/bin/decodeipf.js @@ -3,6 +3,7 @@ if (exec_args[1] == undefined) { return 1 } +const interactive = exec_args[2] && exec_args[2].toLowerCase() == "/i" let infile = files.open(_G.shell.resolvePathInput(exec_args[1]).full) // read input file @@ -49,4 +50,37 @@ graphics.setGraphicsMode(4) graphics.clearText(); graphics.clearPixels(0); graphics.clearPixels2(0) decodefun(ipfbuf, -1048577, -1310721, width, height, hasAlpha) -sys.free(ipfbuf)*/ \ No newline at end of file +sys.free(ipfbuf)*/ + + +let wait = interactive + +if (interactive) { + con.clear(); con.curs_set(0); con.move(1,1) + println("Push and hold Backspace to exit") +} + +let t1 = sys.nanoTime() +let akku = 0 +let notifHideTimer = 0 +const NOTIF_SHOWUPTIME = 3000000000 +while (wait) { + sys.poke(-40, 1) + if (sys.peek(-41) == 67) { + wait = false + con.curs_set(1) + } + + sys.sleep(50) + + let t2 = sys.nanoTime() + akku += (t2 - t1) / 1000000000.0 + + notifHideTimer += (t2 - t1) + if (notifHideTimer > NOTIF_SHOWUPTIME) { + con.clear() + } + + t1 = t2 + +} \ No newline at end of file diff --git a/assets/disk0/tvdos/bin/playmov.js b/assets/disk0/tvdos/bin/playmov.js index e6ad451..b026d49 100644 --- a/assets/disk0/tvdos/bin/playmov.js +++ b/assets/disk0/tvdos/bin/playmov.js @@ -1,5 +1,5 @@ // usage: playmov moviefile.mov [/i] -const interactive = exec_args[2].toLowerCase() == "/i" +const interactive = exec_args[2] && exec_args[2].toLowerCase() == "/i" const WIDTH = 560 const HEIGHT = 448 const FBUF_SIZE = WIDTH * HEIGHT @@ -87,7 +87,7 @@ if (interactive) { println("Push and hold Backspace to exit") } let notifHideTimer = 0 -const NOTIF_SHOWUPTIME = 2000000000 +const NOTIF_SHOWUPTIME = 3000000000 let [cy, cx] = con.getyx() let t1 = sys.nanoTime() renderLoop: @@ -237,7 +237,7 @@ while (!stopPlay && seqread.getReadCount() < FILE_LENGTH) { if (interactive) { notifHideTimer += (t2 - t1) - if (notifHideTimer > NOTIF_SHOWUPTIME) { + if (notifHideTimer > (NOTIF_SHOWUPTIME + frameTime)) { con.clear() } } diff --git a/assets/disk0/tvdos/bin/playpcm.js b/assets/disk0/tvdos/bin/playpcm.js new file mode 100644 index 0000000..20e395a --- /dev/null +++ b/assets/disk0/tvdos/bin/playpcm.js @@ -0,0 +1,162 @@ +// usage: playpcm audiofile.pcm [/i] +let filename = _G.shell.resolvePathInput(exec_args[1]).full +function printdbg(s) { if (0) serial.println(s) } + +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 seqread = require("seqread") +seqread.prepare(filename) + + + + + + +let BLOCK_SIZE = 4096 +let INFILE_BLOCK_SIZE = BLOCK_SIZE +const QUEUE_MAX = 4 // according to the spec + +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 + +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) +if (interactive) { + con.clear(); con.move(1,1) + println("Push and hold Backspace to exit") +} +let [cy, cx] = con.getyx() +let [__, CONSOLE_WIDTH] = con.getmaxyx() +let paintWidth = CONSOLE_WIDTH - 16 +// read chunks loop +readPtr = sys.malloc(BLOCK_SIZE * bitsPerSample / 8) +decodePtr = sys.malloc(BLOCK_SIZE * pcm.HW_SAMPLING_RATE / samplingRate) + + +audio.resetParams(0) +audio.purgeQueue(0) +audio.setPcmMode(0) +audio.setMasterVolume(0, 255) + +let readLength = 1 + +function printPlayBar(startOffset) { + 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(15)) + con.move(cy, 1) + + print(`${secToReadable(currentlySec)} / ${secToReadable(totalSec)}`) + + con.move(cy, 15) + print(' ') + let progressbar = '\x84205u'.repeat(paintWidth + 1) + print(progressbar) + + con.mvaddch(cy, 16 + Math.round(paintWidth * (currently / total)), 0xDB) + } +} +while (!stopPlay && seqread.getReadCount() < FILE_SIZE && readLength > 0) { + if (interactive) { + sys.poke(-40, 1) + if (sys.peek(-41) == 67) { + stopPlay = true + } + } + + + let queueSize = audio.getPosition(0) + if (queueSize <= 1) { + + printPlayBar() + + // upload four samples for lag-safely + for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) { + let remainingBytes = FILE_SIZE - seqread.getReadCount() + + readLength = (remainingBytes < INFILE_BLOCK_SIZE) ? remainingBytes : INFILE_BLOCK_SIZE + if (readLength <= 0) { + printdbg(`readLength = ${readLength}`) + break + } + + printdbg(`offset: ${seqread.getReadCount()}/${FILE_SIZE}; readLength: ${readLength}`) + + seqread.readBytes(readLength, readPtr) + + audio.putPcmDataByPtr(readPtr, readLength, 0) + audio.setSampleUploadLength(0, readLength) + audio.startSampleUpload(0) + + + if (repeat > 1) sys.sleep(10) + + printPlayBar() + } + + audio.play(0) + } + + let remainingBytes = FILE_SIZE - seqread.getReadCount() + printdbg(`readLength = ${readLength}; remainingBytes2 = ${remainingBytes}; seqread.getReadCount() = ${seqread.getReadCount()};`) + + + sys.sleep(10) +} + + +audio.stop(0) +if (readPtr !== undefined) sys.free(readPtr) +if (decodePtr !== undefined) sys.free(decodePtr) diff --git a/assets/disk0/tvdos/bin/playwav.js b/assets/disk0/tvdos/bin/playwav.js index 0f9f447..81e3241 100644 --- a/assets/disk0/tvdos/bin/playwav.js +++ b/assets/disk0/tvdos/bin/playwav.js @@ -2,7 +2,7 @@ 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 interactive = exec_args[2] && exec_args[2].toLowerCase() == "/i" const seqread = require("seqread") const pcm = require("pcm") @@ -104,6 +104,29 @@ if (interactive) { } let [cy, cx] = con.getyx() let [__, CONSOLE_WIDTH] = con.getmaxyx() +let paintWidth = CONSOLE_WIDTH - 16 +function printPlayBar(startOffset) { + 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(15)) + con.move(cy, 1) + + print(`${secToReadable(currentlySec)} / ${secToReadable(totalSec)}`) + + con.move(cy, 15) + print(' ') + let progressbar = '\x84205u'.repeat(paintWidth + 1) + print(progressbar) + + con.mvaddch(cy, 16 + Math.round(paintWidth * (currently / total)), 0xDB) + } +} // read chunks loop while (!stopPlay && seqread.getReadCount() < FILE_SIZE - 8) { let chunkName = seqread.readFourCC() @@ -195,29 +218,8 @@ while (!stopPlay && seqread.getReadCount() < FILE_SIZE - 8) { 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) - } + printPlayBar(startOffset) // upload four samples for lag-safely for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) { @@ -242,6 +244,8 @@ while (!stopPlay && seqread.getReadCount() < FILE_SIZE - 8) { if (repeat > 1) sys.sleep(10) + + printPlayBar(startOffset) } audio.play(0) diff --git a/assets/disk0/tvdos/include/pcm.js b/assets/disk0/tvdos/include/pcm.js index 28ddb55..b19dc52 100644 --- a/assets/disk0/tvdos/include/pcm.js +++ b/assets/disk0/tvdos/include/pcm.js @@ -24,7 +24,7 @@ function s16Tou8(i) { // return s8Tou8((i >> 8) & 255) // apply dithering let ufval = (i / 65536.0) + 0.5 - let ival = randomRound(ufval * 256.0) + let ival = randomRound(ufval * 255.0) return ival|0 } function u16Tos16(i) { return (i > 32767) ? i - 65536 : i } diff --git a/assets/disk0/tvdos/installer/install.js b/assets/disk0/tvdos/installer/install.js index 8a9b486..e15a26b 100644 --- a/assets/disk0/tvdos/installer/install.js +++ b/assets/disk0/tvdos/installer/install.js @@ -1,46 +1,108 @@ +const win = require("wintex") +const [HEIGHt, WIDTH] = con.getmaxyx() + println("let's install!") /* procedure: CANCELLED_BY_USER := - println("Installation of TVDOS was cancelled by the user.") - exit with errorlevel 1 + Untick all sidebar + title: Abort Installation + message: Installation of TVDOS was cancelled by the user. + with button: Quit + on button Quit: exit with errorlevel 1 + +NO_SUITABLE_TARGET := + Untick all sidebar + title: Unable to Install + message: Your system appears to not have a suitable disk for TVDOS installation. Please shut off the power, insert/plug the disk then restart the computer and the installer. + with button: Quit + on button Quit: exit with errorlevel 127 + +Sidebar and Chapter Title := + [] Welcome - License Agreement + [] Customisation - User Interface + [] Disk - Installation Target + [] Summary - Installation Settings + [] Installation - Perform Installation + +var USER_INTERFACE; // "zfm" or "cmd" + +0. Probe all suitable installation candidates +0.1 If there is none, do NO_SUITABLE_TARGET + else, proceed to next step -1. show the list of installable drives. Read-only drives are considered not installable -1.1 if there is at least one installable drives, show the following message: - select drive to install [B,C,D]: -1.2 else, show the following message: - No suitable drives were detected for installing TVDOS. The setup program will exit. (exit with errorlevel 2) +1. Chapter Welcome + show wall of text: + with button: Abort, [], Next + on Abort: do CANCELLED_BY_USER -2. check if the drive has boot sector. if there is, show message: - This drive appears to be bootable, which means there might be other operation system on this drive. - Proceed anyway? [Y/N]: -2.1. if read().trim().toLowercase() is not "y", do CANCELLED_BY_USER -3. show the following message: - In order to install TVDOS to the drive ${destDrive}, the drive must be wiped clean first. - THIS PROCESS WILL IRREVERSIBLY DESTROY ALL THE DATA IN THE DRIVE ${destDrive}! - Type "yes, I consent" to proceed, or type any other text to cancel the installation process: -3.1. if read().trim().toLowercase() is not "yes, i consent" or "yes i consent", do CANCELLED_BY_USER +2. Chapter Customisation + message: Select the default user interface your new system will have. You can change the configuration any time after the installation. + button1: + title: Z File Manager (emph with same goldenrod colour as the focused window frame) + desc: Text-based Graphical Interface for navigating the system using arrow keys + button2: + title: Command-line Interface (emph with same goldenrod colour as the focused window frame) + desc: Traditional DOS experience. Black screen, blinking cursor + with button: Abort, Back Next. + on Abort: do CANCELLED_BY_USER + on Next with button1: set USER_INTERFACE to "zfm" + on Next with button2: set USER_INTERFACE to "cmd" -4. show the following message: - Enter the new name for the drive that TVDOS will be installed: -5. show the following message: - The destination disk will be wiped now. Do not turn off the power... +3. Chapter Disk + message: Choose the disk to install TVDOS. + Selected disk will be cleared only on the actual Installation step. + show buttons on grid arrangement. use template: + title: : // A: B: C: etc + desc: + if clean: This disk is clear + Total bytes + if has bootsector: This disk has bootsector + Used bytes/Total bytes + if has files: This disk has files on it + Used bytes/Total bytes + with button: Abort, Back, Next + on Abort: do CANCELLED_BY_USER + on Next with non-clean disk selected: show popup + heading: Warning + title: Selected disk will be wiped clean and any files on it will be lost permanently. Proceed with installation? + with button: Cancel, Proceed + on Cancel: go back to disk selection + on Proceed: go to next step -6. formatDrive(destDrive, newName, driveNum) -7. show following message: - TVDOS will be installed into the drive ${destDrive}... +4. Chapter Summary + message: This is the overview of the installation. Read carefully before clicking Next. + show wall of text: + Booting + - on Drive : -8. copyFiles(destDrive) + Environment + - start system with (program := if USER_INTERFACE is zfm, "Z File Manager"; is cmd, "Command Line Interface") + - Size of packages to install: kB -9. show following message - TVDOS is successfully installed. You may continue using the Live Boot environment. - To boot from the newly-installed TVDOS, turn off the computer, remove the installation medium, then start the - computer again. (exit with errorlevel 0) + with button: Abort, Back, Next + on Abort: do CANCELLED_BY_USER + + +5. Chapter Installation +5.1 Disk Clear + message: Disk is being cleared, do not turn off the power + formatDrive(destDrive, "TVDOS", driveNum) +5.2 Copy Files + message: Installing TVDOS... + copyFiles(destDrive) + + +6. Still on Chapter Installtion but change title: Installation Was Successful + message: TVDOS is successfully installed. You may continue using the Live Boot environment. + To boot from the new disk, turn off the computer, remove the installation medium, then restart the computer. + with button: [], [], OK + on OK: con.clear(); exit with errorlevel 0 */ diff --git a/assets/disk0/tvdos/tuidev/zfm.js b/assets/disk0/tvdos/tuidev/zfm.js index 180873a..b0a7b89 100644 --- a/assets/disk0/tvdos/tuidev/zfm.js +++ b/assets/disk0/tvdos/tuidev/zfm.js @@ -36,6 +36,9 @@ const EXEC_FUNS = { "wav": (f) => _G.shell.execute(`playwav ${f} /i`), "adpcm": (f) => _G.shell.execute(`playwav ${f} /i`), "mov": (f) => _G.shell.execute(`playmov ${f} /i`), + "pcm": (f) => _G.shell.execute(`playpcm ${f} /i`), + "ipf1": (f) => _G.shell.execute(`decodeipf ${f} /i`), + "ipf2": (f) => _G.shell.execute(`decodeipf ${f} /i`), "bas": (f) => _G.shell.execute(`basic ${f}`) }