diff --git a/assets/disk0/tvdos/bin/playtav.js b/assets/disk0/tvdos/bin/playtav.js
index a3c7906..3f430ba 100644
--- a/assets/disk0/tvdos/bin/playtav.js
+++ b/assets/disk0/tvdos/bin/playtav.js
@@ -2,9 +2,7 @@
// TSVM Advanced Video (TAV) Format Decoder - DWT-based compression
// Adapted from the working playtev.js decoder
// Usage: playtav moviefile.tav [options]
-// Options: -i (interactive), -debug-mv (show motion vector debug visualization)
-// -deinterlace=algorithm (yadif or bwdif, default: yadif)
-// -deblock (enable post-processing deblocking filter)
+// Options: -i (interactive)
const WIDTH = 560
const HEIGHT = 448
@@ -70,7 +68,7 @@ let notifHideTimer = 0
const NOTIF_SHOWUPTIME = 3000000000
let [cy, cx] = con.getyx()
-//let playgui = require("playgui")
+let gui = require("playgui")
let seqread = undefined
let fullFilePathStr = fullFilePath.full
@@ -96,176 +94,9 @@ audio.purgeQueue(0)
audio.setPcmMode(0)
audio.setMasterVolume(0, 255)
-// Subtitle display functions
-function clearSubtitleArea() {
- // Clear the subtitle area at the bottom of the screen
- // Text mode is 80x32, so clear the bottom few lines
- let oldFgColour = con.get_color_fore()
- let oldBgColour = con.get_color_back()
+// set colour zero as half-opaque black
+graphics.setPalette(0, 0, 0, 0, 9)
- con.color_pair(255, 255) // transparent to clear
-
- // Clear bottom 4 lines for subtitles
- for (let row = 29; row <= 32; row++) {
- con.move(row, 1)
- for (let col = 1; col <= 80; col++) {
- print(" ")
- }
- }
-
- con.color_pair(oldFgColour, oldBgColour)
-}
-
-function getVisualLength(line) {
- // Calculate the visual length of a line excluding formatting tags
- let visualLength = 0
- let i = 0
-
- while (i < line.length) {
- if (i < line.length - 2 && line[i] === '<') {
- // Check for formatting tags and skip them
- if (line.substring(i, i + 3).toLowerCase() === '' ||
- line.substring(i, i + 3).toLowerCase() === '') {
- i += 3 // Skip tag
- } else if (i < line.length - 3 &&
- (line.substring(i, i + 4).toLowerCase() === '' ||
- line.substring(i, i + 4).toLowerCase() === '')) {
- i += 4 // Skip closing tag
- } else {
- // Not a formatting tag, count the character
- visualLength++
- i++
- }
- } else {
- // Regular character, count it
- visualLength++
- i++
- }
- }
-
- return visualLength
-}
-
-function displayFormattedLine(line) {
- // Parse line and handle and tags with colour changes
- // Default subtitle colour: yellow (231), formatted text: white (254)
-
- let i = 0
- let inBoldOrItalic = false
-
- // insert initial padding block
- con.color_pair(0, 255)
- con.prnch(0xDE)
- con.color_pair(231, 0)
-
- while (i < line.length) {
- if (i < line.length - 2 && line[i] === '<') {
- // Check for opening tags
- if (line.substring(i, i + 3).toLowerCase() === '' ||
- line.substring(i, i + 3).toLowerCase() === '') {
- con.color_pair(254, 0) // Switch to white for formatted text
- inBoldOrItalic = true
- i += 3
- } else if (i < line.length - 3 &&
- (line.substring(i, i + 4).toLowerCase() === '' ||
- line.substring(i, i + 4).toLowerCase() === '')) {
- con.color_pair(231, 0) // Switch back to yellow for normal text
- inBoldOrItalic = false
- i += 4
- } else {
- // Not a formatting tag, print the character
- print(line[i])
- i++
- }
- } else {
- // Regular character, print it
- print(line[i])
- i++
- }
- }
-
- // insert final padding block
- con.color_pair(0, 255)
- con.prnch(0xDD)
- con.color_pair(231, 0)
-}
-
-function displaySubtitle(text, position = 0) {
- if (!text || text.length === 0) {
- clearSubtitleArea()
- return
- }
-
- // Set subtitle colours: yellow (231) on black (0)
- let oldFgColour = con.get_color_fore()
- let oldBgColour = con.get_color_back()
- con.color_pair(231, 0)
-
- // Split text into lines
- let lines = text.split('\n')
-
- // Calculate position based on subtitle position setting
- let startRow, startCol
- // Calculate visual length without formatting tags for positioning
- let longestLineLength = lines.map(s => getVisualLength(s)).sort().last()
-
- switch (position) {
- case 2: // center left
- case 6: // center right
- case 8: // dead center
- startRow = 16 - Math.floor(lines.length / 2)
- break
- case 3: // top left
- case 4: // top center
- case 5: // top right
- startRow = 2
- break
- case 0: // bottom center
- case 1: // bottom left
- case 7: // bottom right
- default:
- startRow = 32 - lines.length
- startRow = 32 - lines.length
- startRow = 32 - lines.length // Default to bottom center
- }
-
- // Display each line
- for (let i = 0; i < lines.length; i++) {
- let line = lines[i].trim()
- if (line.length === 0) continue
-
- let row = startRow + i
- if (row < 1) row = 1
- if (row > 32) row = 32
-
- // Calculate column based on alignment
- switch (position) {
- case 1: // bottom left
- case 2: // center left
- case 3: // top left
- startCol = 1
- break
- case 5: // top right
- case 6: // center right
- case 7: // bottom right
- startCol = Math.max(1, 78 - getVisualLength(line) - 2)
- break
- case 0: // bottom center
- case 4: // top center
- case 8: // dead center
- default:
- startCol = Math.max(1, Math.floor((80 - longestLineLength - 2) / 2) + 1)
- break
- }
-
- con.move(row, startCol)
-
- // Parse and display line with formatting tag support
- displayFormattedLine(line)
- }
-
- con.color_pair(oldFgColour, oldBgColour)
-}
function processSubtitlePacket(packetSize) {
// Read subtitle packet data according to SSF format
@@ -298,7 +129,7 @@ function processSubtitlePacket(packetSize) {
sys.free(textBytes)
subtitleText = textStr
subtitleVisible = true
- displaySubtitle(subtitleText, subtitlePosition)
+ gui.displaySubtitle(subtitleText, subtitlePosition)
}
break
}
@@ -306,7 +137,7 @@ function processSubtitlePacket(packetSize) {
case SSF_OP_HIDE: {
subtitleVisible = false
subtitleText = ""
- clearSubtitleArea()
+ gui.clearSubtitleArea()
break
}
@@ -320,8 +151,8 @@ function processSubtitlePacket(packetSize) {
// Re-display current subtitle at new position if visible
if (subtitleVisible && subtitleText.length > 0) {
- clearSubtitleArea()
- displaySubtitle(subtitleText, subtitlePosition)
+ gui.clearSubtitleArea()
+ gui.displaySubtitle(subtitleText, subtitlePosition)
}
}
}
@@ -809,24 +640,15 @@ try {
if (interactive) {
notifHideTimer += (t2 - t1)
if (!notifHidden && notifHideTimer > (NOTIF_SHOWUPTIME + FRAME_TIME)) {
- con.move(1, 1)
- print(' '.repeat(79))
+ // clearing function here
notifHidden = true
}
- if (!hasSubtitles) {
- con.move(31, 1)
- con.color_pair(253, 0)
- if (currentFileIndex > 1) {
- print(`File ${currentFileIndex}: ${frameCount}/${header.totalFrames} (qY=${decoderDbgInfo.qY}, ${((frameCount / akku2 * 100)|0) / 100}f) `)
- } else {
- print(`Frame: ${frameCount}/${header.totalFrames} (qY=${decoderDbgInfo.qY}, ${((frameCount / akku2 * 100)|0) / 100}f) `)
- }
- con.move(32, 1)
- con.color_pair(253, 0)
- print(`VRate: ${(getVideoRate() / 1024 * 8)|0} kbps `)
- con.move(1, 1)
- }
+ con.move(32, 1)
+ con.color_pair(253, 0)
+ print(`B=${(getVideoRate() / 1024 * 8)|0}k F=${frameCount}/${header.totalFrames} qY=${decoderDbgInfo.qY} f=${(frameCount / akku2).toFixed(2)}`)
+ con.color_pair(255, 255);print(" ")
+ con.move(1, 1)
}
t1 = t2
diff --git a/assets/disk0/tvdos/include/playgui.mjs b/assets/disk0/tvdos/include/playgui.mjs
new file mode 100644
index 0000000..ca027e7
--- /dev/null
+++ b/assets/disk0/tvdos/include/playgui.mjs
@@ -0,0 +1,178 @@
+// Common GUI for media player
+// Created by CuriousTorvald on 2025-09-30.
+
+// Subtitle display functions
+function clearSubtitleArea() {
+ // Clear the subtitle area at the bottom of the screen
+ // Text mode is 80x32, so clear the bottom few lines
+ let oldFgColour = con.get_color_fore()
+ let oldBgColour = con.get_color_back()
+
+ con.color_pair(255, 255) // transparent to clear
+
+ // Clear bottom 4 lines for subtitles
+ for (let row = 28; row <= 31; row++) {
+ con.move(row, 1)
+ for (let col = 1; col <= 80; col++) {
+ print(" ")
+ }
+ }
+
+ con.color_pair(oldFgColour, oldBgColour)
+}
+
+function getVisualLength(line) {
+ // Calculate the visual length of a line excluding formatting tags
+ let visualLength = 0
+ let i = 0
+
+ while (i < line.length) {
+ if (i < line.length - 2 && line[i] === '<') {
+ // Check for formatting tags and skip them
+ if (line.substring(i, i + 3).toLowerCase() === '' ||
+ line.substring(i, i + 3).toLowerCase() === '') {
+ i += 3 // Skip tag
+ } else if (i < line.length - 3 &&
+ (line.substring(i, i + 4).toLowerCase() === '' ||
+ line.substring(i, i + 4).toLowerCase() === '')) {
+ i += 4 // Skip closing tag
+ } else {
+ // Not a formatting tag, count the character
+ visualLength++
+ i++
+ }
+ } else {
+ // Regular character, count it
+ visualLength++
+ i++
+ }
+ }
+
+ return visualLength
+}
+
+function displayFormattedLine(line) {
+ // Parse line and handle and tags with colour changes
+ // Default subtitle colour: yellow (231), formatted text: white (254)
+
+ let i = 0
+ let inBoldOrItalic = false
+
+ // insert initial padding block
+ con.color_pair(0, 255)
+ con.prnch(0xDE)
+ con.color_pair(231, 0)
+
+ while (i < line.length) {
+ if (i < line.length - 2 && line[i] === '<') {
+ // Check for opening tags
+ if (line.substring(i, i + 3).toLowerCase() === '' ||
+ line.substring(i, i + 3).toLowerCase() === '') {
+ con.color_pair(254, 0) // Switch to white for formatted text
+ inBoldOrItalic = true
+ i += 3
+ } else if (i < line.length - 3 &&
+ (line.substring(i, i + 4).toLowerCase() === '' ||
+ line.substring(i, i + 4).toLowerCase() === '')) {
+ con.color_pair(231, 0) // Switch back to yellow for normal text
+ inBoldOrItalic = false
+ i += 4
+ } else {
+ // Not a formatting tag, print the character
+ print(line[i])
+ i++
+ }
+ } else {
+ // Regular character, print it
+ print(line[i])
+ i++
+ }
+ }
+
+ // insert final padding block
+ con.color_pair(0, 255)
+ con.prnch(0xDD)
+ con.color_pair(231, 0)
+}
+
+function displaySubtitle(text, position = 0) {
+ if (!text || text.length === 0) {
+ clearSubtitleArea()
+ return
+ }
+
+ // Set subtitle colours: yellow (231) on black (0)
+ let oldFgColour = con.get_color_fore()
+ let oldBgColour = con.get_color_back()
+ con.color_pair(231, 0)
+
+ // Split text into lines
+ let lines = text.split('\n')
+
+ // Calculate position based on subtitle position setting
+ let startRow, startCol
+ // Calculate visual length without formatting tags for positioning
+ let longestLineLength = lines.map(s => getVisualLength(s)).sort().last()
+
+ switch (position) {
+ case 2: // center left
+ case 6: // center right
+ case 8: // dead center
+ startRow = 16 - Math.floor(lines.length / 2)
+ break
+ case 3: // top left
+ case 4: // top center
+ case 5: // top right
+ startRow = 2
+ break
+ case 0: // bottom center
+ case 1: // bottom left
+ case 7: // bottom right
+ default:
+ startRow = 31 - lines.length
+ startRow = 31 - lines.length
+ startRow = 31 - lines.length // Default to bottom center
+ }
+
+ // Display each line
+ for (let i = 0; i < lines.length; i++) {
+ let line = lines[i].trim()
+ if (line.length === 0) continue
+
+ let row = startRow + i
+ if (row < 1) row = 1
+ if (row > 32) row = 32
+
+ // Calculate column based on alignment
+ switch (position) {
+ case 1: // bottom left
+ case 2: // center left
+ case 3: // top left
+ startCol = 1
+ break
+ case 5: // top right
+ case 6: // center right
+ case 7: // bottom right
+ startCol = Math.max(1, 78 - getVisualLength(line) - 2)
+ break
+ case 0: // bottom center
+ case 4: // top center
+ case 8: // dead center
+ default:
+ startCol = Math.max(1, Math.floor((80 - longestLineLength - 2) / 2) + 1)
+ break
+ }
+
+ con.move(row, startCol)
+
+ // Parse and display line with formatting tag support
+ displayFormattedLine(line)
+ }
+
+ con.color_pair(oldFgColour, oldBgColour)
+}
+
+exports = {
+ clearSubtitleArea,
+ displaySubtitle
+}
\ No newline at end of file
diff --git a/video_encoder/encoder_tav.c b/video_encoder/encoder_tav.c
index 1b64ea9..aa85044 100644
--- a/video_encoder/encoder_tav.c
+++ b/video_encoder/encoder_tav.c
@@ -3219,15 +3219,15 @@ int main(int argc, char *argv[]) {
enc->target_bitrate = bitrate;
// Choose initial q-index based on target bitrate
- if (bitrate >= 64000) {
+ if (bitrate >= 32000) {
enc->quality_level = 5;
- } else if (bitrate >= 32000) {
- enc->quality_level = 4;
} else if (bitrate >= 16000) {
- enc->quality_level = 3;
+ enc->quality_level = 4;
} else if (bitrate >= 8000) {
- enc->quality_level = 2;
+ enc->quality_level = 3;
} else if (bitrate >= 4000) {
+ enc->quality_level = 2;
+ } else if (bitrate >= 2000) {
enc->quality_level = 1;
} else {
enc->quality_level = 0;