From c5bc0d652615a9f696567cf66da82816b48fce78 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Wed, 22 Apr 2026 23:03:33 +0900 Subject: [PATCH] tauty --- assets/disk0/tvdos/bin/taut.js | 186 +++++++++++++++++++++++++++++++-- terranmon.txt | 54 +++++++--- 2 files changed, 219 insertions(+), 21 deletions(-) diff --git a/assets/disk0/tvdos/bin/taut.js b/assets/disk0/tvdos/bin/taut.js index 865c8f6..9d8b70d 100644 --- a/assets/disk0/tvdos/bin/taut.js +++ b/assets/disk0/tvdos/bin/taut.js @@ -9,6 +9,8 @@ const font = require("font") font.setHighRom("A:/tvdos/bin/tautfont_high.chr") +const MIDDOT = "\u00FA" + const sym = { /* accidentals */ accnull:"\u00A2\u00A3", @@ -39,9 +41,23 @@ doubledntick:"\u009D", keyoff:"\u00A0\u00CD\u00CD\u00A1", notecut:"\u00A4\u00A4\u00A4\u00A4", +/* special effects */ +volset:MIDDOT, +volup:"\u008430u", +voldn:"\u008431u", +volfineup:"+", +volfinedn:"-", + +panset:MIDDOT, +panle:"\u008417u", +panri:"\u008416u", +panfinele:"\u008427u", +panfineri:"\u008426u", + /* miscellaneous */ unticked:"\u009E", ticked:"\u009F", +middot:MIDDOT } const pitchTablePresets = { @@ -88,13 +104,167 @@ sym:[`C${sym.accnull}`,`C${sym.sharp}`,`D${sym.accnull}`,`D${sym.sharp}`,`E${sym } -let intv = [120,240,410,530,960] -intv.forEach(i => { - let preset = pitchTablePresets[i] - println(preset.name+':') - preset.sym.forEach(s => print(s+'3\t')) - println() -}) -print(`${sym.keyoff}\t${sym.notecut}`); println() +const volEffSym = [sym.volset, sym.volup, sym.voldn, sym.volfineup, sym.volfinedn] +const panEffSym = [sym.panset, sym.panle, sym.panri, sym.panfinele, sym.panfineri] +const colNote = 239 +const colInst = 114 +const colVol = 117 +const colPan = 221 +const colEffOp = 208 +const colEffArg = 231 +const colBackPtn = 255 + +/** + * Prints out pattern data at current cursor position, assuming indexFrom and indexTo does not overflow current screen size + */ +function printPattern(patternData, indexFrom, indexTo, drawMode) { + const off = 8*indexFrom + + const note = patternData[off] | (patternData[off+1] << 8) + const inst = patternData[off+2] + const voleff = patternData[off+3] + const voleffarg = voleff & 63 + const paneff = patternData[off+4] + const paneffarg = paneff & 63 + const effop = patternData[off+5] + const effarg = patternData[off+6] | (patternData[off+7] << 8) + + let sNote = note.toString(16).toUpperCase().padStart(4,'0') + if (note == 0xFFFF) sNote = sym.middot.repeat(4) + if (note == 0xFFFE) sNote = sym.notecut + if (note == 0x0000) sNote = sym.keyoff + let sInst = inst.toString(16).toUpperCase().padStart(2,sym.middot) + if (inst == 0) sInst = sym.middot.repeat(3); + let sVolEff = volEffSym[voleff >>> 6] + let sVolArg = voleffarg.toString().padStart(2,sym.middot) + // fine slide notation + if (voleff >>> 6 == 3) { + if (voleffarg == 0) { + sVolEff = sym.middot + sVolArg = sym.middot.repeat(2) + } + else if (voleffarg >= 32) { + sVolEff = volEffSym[3] + sVolArg = (voleffarg & 31).toString().padStart(2,'0') + } + else { + sVolEff = volEffSym[4] + sVolArg = (voleffarg & 31).toString().padStart(2,'0') + } + } + let sPanEff = panEffSym[voleff >>> 6] + let sPanArg = paneffarg.toString().padStart(2,sym.middot) + // fine slide notation + if (paneff >>> 6 == 3) { + if (paneffarg == 0) { + sPanEff = sym.middot + sPanArg = sym.middot.repeat(2) + } + else if (paneffarg >= 32) { + sPanEff = panEffSym[4] + sPanArg = (paneffarg & 31).toString().padStart(2,'0') + } + else { + sPanEff = panEffSym[3] + sPanArg = (paneffarg & 31).toString().padStart(2,'0') + } + } + let sEffOp = effop.toString(36).toUpperCase()[0] + let sEffArg = effarg.toString(16).toUpperCase().padStart(4,'0') + if (sEffOp == 0 && sEffArg == 0) { + sEffOp = sym.middot + sEffArg = sym.middot.repeat(4) + } + + let [cy, cx] = con.getyx() + + for (let i = 0; i < indexTo; i++) { + con.move(cy + i, cx) + + con.color_pair(colNote, colBackPtn) + print(sNote) + + con.color_pair(colInst, colBackPtn) + print(sInst) + + con.color_pair(colVol, colBackPtn) + print(sVolEff) + con.color_pair(colVol, colBackPtn) + print(sVolArg) + + con.color_pair(colPan, colBackPtn) + print(sPanEff) + con.color_pair(colPan, colBackPtn) + print(sPanArg) + + con.color_pair(colEffOp, colBackPtn) + print(sEffOp) + con.color_pair(colEffArg, colBackPtn) + print(sEffArg) + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// GUI DEFINITION +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// drawing constants +const [SCRH, SCRW] = con.getmaxyx() +const PTNVIEW_OFFSET_X = 10 +const PTNVIEW_OFFSET_Y = 4 +const PTNVIEW_HEIGHT = SCRH - PTNVIEW_OFFSET_Y +const COLSIZE = 18 +const VOCSIZE = 4 + +const VIEW_TIMELINE = 0 +const VIEW_ORDERS = 1 +const VIEW_INSTRUMENT = 2 +const VIEW_PATTERN_DETAILS = 3 + +// draw functions +function drawPatternView() { + for (let c = 0; c < VOCSIZE; c++) { + con.move(PTNVIEW_OFFSET_Y,PTNVIEW_OFFSET_X+COLSIZE*c) + printPattern(fakeData, 0, PTNVIEW_HEIGHT) + + // separator + if (c < VOCSIZE - 1) { + con.color_pair(252,255) + for (let y = 0; y < PTNVIEW_HEIGHT; y++) { + con.move(PTNVIEW_OFFSET_Y+y,PTNVIEW_OFFSET_X+COLSIZE*(c+1)-1) + con.prnch(0xB3) + } + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// APPLICATION STUB +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// nav constants +const KEY_LEFT = 21 +const KEY_RIGHT = 22 +const KEY_UP = 19 +const KEY_DOWN = 20 +const KEY_RETURN = 66 +const KEY_BKSP = 67 +const KEY_TAB = 61 + +// GUI status +let currentPanel = VIEW_TIMELINE + +// app run +let fakeData = new Uint8Array(512) +for (let i = 0; i < 512; i++) { + fakeData[i] = (Math.random(sys.nanoTime())*256)&255 +} + +con.clear() +if (currentPanel == VIEW_TIMELINE) { + drawPatternView() +} +con.move(1,1) \ No newline at end of file diff --git a/terranmon.txt b/terranmon.txt index 4d4f421..b17dd1c 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -2180,6 +2180,8 @@ Created by CuriousTorvald on 2026-04-19 This is a file format for Taud tracker data. +Endianness: Little + # File Structure \x1F T S V M a u d [HEADER] @@ -2207,7 +2209,9 @@ Rows of 16 bytes: Uint8 Number of voices Uint16 Number of patterns (0 is invalid. pattern bin length = numPats * 8 bytes) Uint8 Initial BPM (bias of -24. 0x00=24, 0xFF=279) - Uint8 Initial Tickrate(0 is invalid) + Uint8 Initial Tickrate (0 is invalid) + Uint16 Current Tuning base note (0-4095), assuming octave 3. C3 (the default value) is 0x4000 + Float32 Frequency at the base note. Default (A440) is 261.6255653 Byte[7] Reserved for future versions Taud device can queue up to 2 "playdata" in its buffer, which can be interpreted as a song. @@ -2217,6 +2221,8 @@ Taud device can queue up to 2 "playdata" in its buffer, which can be interpreted **Taud Project Format** is an extension to Taud format Created by CuriousTorvald on 2026-04-22 +Endianness: Little + # File Structure \x1F T S V M a u d [HEADER] (modified) @@ -2242,9 +2248,7 @@ Created by CuriousTorvald on 2026-04-22 ## Project Data Byte[8] Magic (\x1E T a u d P r J) - Uint16 Pitch table preset - Uint16 Current Tuning base note (0-4095), assuming octave 3 - Uint16 Frequency at the base note (Float16) + Byte[8] Reserved * Repetition of Byte[4] Title of the section (fourcc) Uint32 Section length @@ -2252,13 +2256,39 @@ Created by CuriousTorvald on 2026-04-22 ### Predefined sections -* PCom. Project composer/author -* PCpr. Project copyright string -* PNam. Project name -* PNot. Custom notation definition +prefixes: + +- P: Project +- I: Instrument +- p: Pattern +- S: Sample +- s: Song + +* PCom. Project author. Encoding: UTF-8 +* PCpr. Project copyright string. Encoding: UTF-8 +* PNam. Project name. Encoding: UTF-8 + +* INam. Instrument name table. Strings separated by comma + +* pNam. Pattern name table. Strings separated by comma + +* SNam. Sample name table. Strings separated by comma + +* sMet. Song metadata table + * Repetition of: + Uint8 Song index + Uint32 Size of this table following this field + Uint16 Notation used for this song (takes notation index) + Byte[*] Song name, null terminated. Encoding: UTF-8 + Byte[*] Song composer, null terminated. Encoding: UTF-8 + Byte[*] Song copyright string, null terminated. Encoding: UTF-8 + +* nota. Custom notation definition + * Repetition of: + Uint8 Notation index (starting from zero) used by songs + Uint32 Size of this notation following this field Uint8 Flags 0b nnnn 000t - n: notation index (starting from zero) used by a song. You can have 16 different custom notation t: NOT using interval system (you are responsible for defining every notes expressible) Uint8 Reserved Float32 Interval size (octave system = 2.0f). If Flag 't' is set, this must be NaN. 0f and Infinity are considered illegal @@ -2266,13 +2296,11 @@ Created by CuriousTorvald on 2026-04-22 Byte[8] Reserved Byte[*] Name, null terminated. Encoding: UTF-8 Byte[*] Notation table. Comma-separated and null-terminated. Encoding: raw bytes - Uint16[*] Frequency table. Size of the table is defined by "Notes between interval MINUS ONE" + Uint16[*] Frequency table. Size of the table is defined by "Notes between interval MINUS ONE". All relative to the base note (Song table will be referred), in 4096-TET note number. Index zero of this table will be 0x0 if you read the spec right Note: custom notations will use internal index 65535 down to 65520 (index 0 = 65535, index 15 = 65520) -* INam. Instrument name table. Strings separated by comma -* SNam. Sample name table. Strings separated by comma -* TNam. Pattern name table. Strings separated by comma + --------------------------------------------------------------------------------